mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into fix/interface-console
This commit is contained in:
commit
b664fea758
69 changed files with 1616 additions and 565 deletions
308
.github/workflows/master_build.yml
vendored
308
.github/workflows/master_build.yml
vendored
|
@ -3,114 +3,60 @@ name: Master CI Build
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- gha-master-ci
|
||||
# FIXME: Change target branch to "master" before merging into "master" branch.
|
||||
|
||||
env:
|
||||
#APP_NAME: gpu-frame-player
|
||||
APP_NAME: interface
|
||||
BUILD_TYPE: Release
|
||||
BUCKET_NAME: hifi-gh-builds
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
CI_BUILD: Github
|
||||
CMAKE_BACKTRACE_URL: https://highfidelity.sp.backtrace.io:6098
|
||||
CMAKE_BACKTRACE_TOKEN: ${{ secrets.backtrace_token }}
|
||||
CMAKE_BACKTRACE_SYMBOLS_TOKEN: ${{ secrets.backtrace_symbols_token }}
|
||||
GIT_COMMIT: ${{ github.sha }}
|
||||
HIFI_VCPKG_BOOTSTRAP: true
|
||||
LAUNCHER_HMAC_SECRET: ${{ secrets.launcher_hmac_secret }}
|
||||
OCULUS_APP_ID: '${{ secrets.oculus_app_id }}'
|
||||
# VCPKG did not build well on OSX disabling HIFI_VCPKG_BOOTSTRAP, which invokes a download to a working version of vcpkg
|
||||
# HIFI_VCPKG_BOOTSTRAP: true
|
||||
RELEASE_TYPE: PRODUCTION
|
||||
RELEASE_DYNAMODB_V2: ReleaseManager2-ReleaseQueue-prod
|
||||
RELEASE_NUMBER: ${{ github.run_number }}
|
||||
STABLE_BUILD: 0
|
||||
UPLOAD_BUCKET: athena-public
|
||||
|
||||
|
||||
# OSX specific variables
|
||||
# OSX-specific variables
|
||||
DEVELOPER_DIR: /Applications/Xcode_11.2.app/Contents/Developer
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.11'
|
||||
|
||||
# WIN32 specific variables
|
||||
# WIN-specific variables
|
||||
PreferredToolArchitecture: X64
|
||||
|
||||
# Mac OS
|
||||
#PLATFORM_CMAKE_GENERATOR=Xcode
|
||||
#PLATFORM_BUILD_ARGUMENTS=--config Release --target package
|
||||
#ARTIFACT_EXPRESSION=build/*.dmg,build/*.zip
|
||||
|
||||
# Windows
|
||||
#PLATFORM_CMAKE_GENERATOR=Visual Studio 15 2017 Win64
|
||||
#PLATFORM_BUILD_ARGUMENTS=--target package --config release
|
||||
#ARTIFACT_EXPRESSION=build/*.exe,build/*.zip,*-symbols.zip
|
||||
|
||||
# Ubuntu
|
||||
#PLATFORM_CMAKE_GENERATOR=Unix Makefiles
|
||||
#PLATFORM_BUILD_ARGUMENTS=--target all -- -j4
|
||||
#ARTIFACT_EXPRESSION=build/assignment-client/**,build/domain-server/**,build/ice-server/ice-server,build/tools/ice-client/ice-client,build/tools/ac-client/ac-client,build/tools/oven,build/ext/makefiles/nvtt/project/lib/**,build/ext/makefiles/quazip/project/lib/**
|
||||
|
||||
# Android
|
||||
# branch: master
|
||||
# GA_TRACKING_ID: ${{ secrets.ga_tracking_id }}
|
||||
# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_NIGHTLY}
|
||||
# ANDROID_OAUTH_CLIENT_ID=6c7d2349c0614640150db37457a1f75dce98a28ffe8f14d47f6cfae4de5b262a
|
||||
# ANDROID_OAUTH_REDIRECT_URI=https://dev-android-interface.highfidelity.com/auth
|
||||
# branch: !master
|
||||
# GA_TRACKING_ID=UA-39558647-11
|
||||
# ANDROID_OAUTH_CLIENT_SECRET=${MASKED_ANDROID_OAUTH_CLIENT_SECRET_RELEASE}
|
||||
# ANDROID_OAUTH_CLIENT_ID= c1063ea5d0b0c405e0c9cd77351328e211a91496a3f25985a99e861f1661db1d
|
||||
# ANDROID_OAUTH_REDIRECT_URI=https://android-interface.highfidelity.com/auth
|
||||
# ARTIFACT_EXPRESSION=android/*.apk
|
||||
# ANDROID_APK_NAME=HighFidelity-Beta-PR${RELEASE_NUMBER}-${GIT_COMMIT_SHORT}.apk
|
||||
# ANDROID_BUILT_APK_NAME=interface-debug.apk
|
||||
# ANDROID_APP=interface
|
||||
# ANDROID_BUILD_DIR=debug
|
||||
# ANDROID_BUILD_TARGET=assembleDebug
|
||||
# STABLE_BUILD=0
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
generate_build_number:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate build number
|
||||
id: buildnumber
|
||||
uses: highfidelity/build-number@v3
|
||||
with:
|
||||
token: ${{secrets.github_token}}
|
||||
- name: Upload build number
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: BUILD_NUMBER
|
||||
path: BUILD_NUMBER
|
||||
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macOS-latest]
|
||||
build_type: [full, client]
|
||||
#os: [windows-latest, macOS-latest, ubuntu-latest]
|
||||
# exclude:
|
||||
# - os: ubuntu-latest
|
||||
# build_type: client
|
||||
os: [windows-latest, macOS-latest, ubuntu-18.04]
|
||||
# build_type: [full, client]
|
||||
build_type: [full]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
build_type: full
|
||||
apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0
|
||||
fail-fast: false
|
||||
runs-on: ${{matrix.os}}
|
||||
needs: generate_build_number
|
||||
steps:
|
||||
- name: Download build number
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: BUILD_NUMBER
|
||||
- name: Restore build number
|
||||
id: buildnumber
|
||||
uses: highfidelity/build-number@v3
|
||||
with:
|
||||
output_name: RELEASE_NUMBER
|
||||
- name: Configure Build Environment 1
|
||||
- name: Report Build Number
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Build number: $BUILD_NUMBER"
|
||||
- name: Configure build environment 1
|
||||
shell: bash
|
||||
id: buildenv1
|
||||
run: |
|
||||
echo ::set-env name=UPLOAD_PREFIX::master
|
||||
echo ::set-env name=GIT_COMMIT_SHORT::`echo $GIT_COMMIT | cut -c1-7`
|
||||
echo ::set-env name=JOB_NAME::"build (${{matrix.os}}, ${{matrix.build_type}})"
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
if [[ "${{ matrix.os }}" = "ubuntu-"* ]]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=INSTALLER_EXT::tgz
|
||||
echo ::set-env name=CMAKE_BUILD_EXTRA::"-- -j3"
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)"
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
|
@ -118,8 +64,8 @@ jobs:
|
|||
echo ::set-env name=ZIP_COMMAND::zip
|
||||
echo ::set-env name=ZIP_ARGS::-r
|
||||
echo ::set-env name=INSTALLER_EXT::dmg
|
||||
echo ::set-env name=SYMBOL_REGEX::dSYM
|
||||
echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
|
@ -127,40 +73,28 @@ jobs:
|
|||
echo ::set-env name=ZIP_COMMAND::7z
|
||||
echo ::set-env name=ZIP_ARGS::a
|
||||
echo ::set-env name=INSTALLER_EXT::exe
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
echo "::set-env name=SYMBOL_REGEX::\(exe\|dll\|pdb\)"
|
||||
echo "::set-output name=symbols_archive::${{ steps.buildnumber.outputs.build_number }}-${{ matrix.build_type }}-win-symbols.zip"
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip"
|
||||
# echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
|
||||
# echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
|
||||
fi
|
||||
# Configuration is broken into two steps because you can't set an env var and also reference it in the same step
|
||||
- name: Configure Build Environment 2
|
||||
- name: Configure build environment 2
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
echo ::set-env name=ARTIFACT_PATTERN::HighFidelity-Beta-*.$INSTALLER_EXT
|
||||
echo ::set-env name=ARTIFACT_PATTERN::Vircadia-Alpha-*.$INSTALLER_EXT
|
||||
# Build type variables
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo ::set-env name=CLIENT_ONLY::FALSE
|
||||
echo ::set-env name=INSTALLER::HighFidelity-Beta-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
else
|
||||
echo ::set-env name=CLIENT_ONLY::TRUE
|
||||
echo ::set-env name=INSTALLER::HighFidelity-Beta-Interface-$RELEASE_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
echo ::set-env name=INSTALLER::Vircadia-Alpha-Interface-$BUILD_NUMBER-$GIT_COMMIT_SHORT.$INSTALLER_EXT
|
||||
fi
|
||||
# Linux build variables
|
||||
if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
|
||||
echo ::set-env name=PYTHON_EXEC::python3
|
||||
echo ::set-env name=CMAKE_EXTRA::""
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-latest" ]; then
|
||||
echo ::set-env name=CMAKE_EXTRA::"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -G Xcode"
|
||||
fi
|
||||
# Windows build variables
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
echo ::set-env name=CMAKE_EXTRA::"-A x64"
|
||||
echo ::set-env name=HF_PFX_PASSPHRASE::${{secrets.pfx_key}}
|
||||
echo "::set-env name=HF_PFX_FILE::${{runner.workspace}}\build\codesign.pfx"
|
||||
fi
|
||||
- name: Clear Working Directory
|
||||
if: matrix.os == 'windows-latest'
|
||||
- name: Clear working directory
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: rm -rf ./*
|
||||
|
@ -168,89 +102,119 @@ jobs:
|
|||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Create Build Directory
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Decrypt Signing Key (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
- name: Install dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
shell: bash
|
||||
run: gpg --batch --yes -o codesign.pfx --passphrase "${{secrets.gpg_symmetric_key}}" --decrypt $GITHUB_WORKSPACE/tools/ci-scripts/codesign.pfx.gpg
|
||||
- name: Import Signing Key (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: powershell
|
||||
run: |
|
||||
$mypwd=ConvertTo-SecureString -String ${{ secrets.pfx_key }} -Force -AsPlainText
|
||||
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\CurrentUser\My -FilePath ${{runner.workspace}}\build\codesign.pfx
|
||||
Import-PfxCertificate -Password $mypwd -CertStoreLocation Cert:\LocalMachine\My -FilePath ${{runner.workspace}}\build\codesign.pfx
|
||||
run: |
|
||||
echo "Installing Python Modules:"
|
||||
pip3 install distro || exit 1
|
||||
echo "Updating apt repository index"
|
||||
sudo apt update || exit 1
|
||||
echo "Installing apt packages"
|
||||
sudo apt install -y ${{ matrix.apt-dependencies }} || exit 1
|
||||
- name: Install Python modules
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
shell: bash
|
||||
run: $PYTHON_EXEC -m pip install boto3 PyGithub
|
||||
- name: Create build environment
|
||||
shell: bash
|
||||
run: cmake -E make_directory "${{runner.workspace}}/build"
|
||||
- name: Configure CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY $CMAKE_EXTRA
|
||||
- name: Build Application
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY -DBYPASS_SIGNING:BOOLEAN=TRUE $CMAKE_EXTRA
|
||||
- name: Build application
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME
|
||||
- name: Build Console
|
||||
run: cmake --build . --config $BUILD_TYPE --target $APP_NAME $CMAKE_BUILD_EXTRA
|
||||
- name: Build domain server
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console
|
||||
- name: Build Domain Server (FullBuild)
|
||||
if: matrix.build_type == 'full'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server
|
||||
- name: Build Assignment Client (FullBuild)
|
||||
if: matrix.build_type == 'full'
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client
|
||||
- name: Build Installer
|
||||
run: cmake --build . --config $BUILD_TYPE --target domain-server $CMAKE_BUILD_EXTRA
|
||||
- name: Build assignment client
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target package
|
||||
- name: Sign Installer (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64
|
||||
run: .\signtool.exe sign /fd sha256 /f ${{runner.workspace}}\build\codesign.pfx /p ${{secrets.pfx_key}} /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${{runner.workspace}}\build\${env:INSTALLER}
|
||||
- name: Upload Artifact
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
run: cmake --build . --config $BUILD_TYPE --target assignment-client $CMAKE_BUILD_EXTRA
|
||||
- name: Build console
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target packaged-server-console $CMAKE_BUILD_EXTRA
|
||||
- name: Build installer
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Retry code from https://unix.stackexchange.com/a/137639"
|
||||
function fail {
|
||||
echo $1 >&2
|
||||
exit 1
|
||||
}
|
||||
function retry {
|
||||
local n=1
|
||||
local max=5
|
||||
local delay=15
|
||||
while true; do
|
||||
"$@" && break || {
|
||||
if [[ $n -lt $max ]]; then
|
||||
((n++))
|
||||
echo "Command failed. Attempt $n/$max:"
|
||||
sleep $delay;
|
||||
else
|
||||
fail "The command has failed after $n attempts."
|
||||
fi
|
||||
}
|
||||
done
|
||||
}
|
||||
retry cmake --build . --config $BUILD_TYPE --target package $CMAKE_BUILD_EXTRA
|
||||
#- name: Sign installer (Windows)
|
||||
# if: startsWith(matrix.os, 'windows')
|
||||
# shell: powershell
|
||||
# working-directory: C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64
|
||||
# run: .\signtool.exe sign /fd sha256 /f ${{runner.workspace}}\build\codesign.pfx /p ${{secrets.pfx_key}} /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${{runner.workspace}}\build\${env:INSTALLER}
|
||||
- name: Output system stats
|
||||
if: ${{ always() }}
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Disk usage:"
|
||||
df -h
|
||||
- name: Output installer logs
|
||||
if: failure() && startsWith(matrix.os, 'windows')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cat ./_CPack_Packages/win64/NSIS/NSISOutput.log
|
||||
- name: Upload artifact
|
||||
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.aws_access_key_id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.aws_secret_access_key }}
|
||||
run: $PYTHON_EXEC $GITHUB_WORKSPACE/tools/ci-scripts/upload.py
|
||||
- name: Archive Symbols
|
||||
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
working-directory: ${{runner.workspace}}
|
||||
shell: bash
|
||||
run: |
|
||||
SYMBOLS_TEMP="symbols-temp"
|
||||
mkdir $SYMBOLS_TEMP
|
||||
find "./build" \( -path '*/tools/gpu-frame-player/*' -or -path '*/interface/*' -or -path '*/plugins/*' \) -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \;
|
||||
cd $SYMBOLS_TEMP
|
||||
$ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} .
|
||||
- name: Upload Symbols
|
||||
if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
working-directory: ${{runner.workspace}}
|
||||
shell: bash
|
||||
run: |
|
||||
curl --data-binary @${{ steps.buildenv1.outputs.symbols_archive }} "$CMAKE_BACKTRACE_URL/post?format=symbols&token=$CMAKE_BACKTRACE_SYMBOLS_TOKEN&upload_file=${{steps.buildenv1.outputs.symbols_archive}}&tag=$RELEASE_NUMBER"
|
||||
# - name: Debug List Symbols
|
||||
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
# - name: Debug Upload Symbols Artifact
|
||||
# if: (matrix.os == 'windows-latest') || (matrix.os == 'macOS-latest')
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# name: symbols
|
||||
# path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}
|
||||
#- name: Archive symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# SYMBOLS_TEMP="symbols-temp"
|
||||
# mkdir $SYMBOLS_TEMP
|
||||
# find "./build" \( -path '*/tools/gpu-frame-player/*' -or -path '*/interface/*' -or -path '*/plugins/*' \) -regex ".*\.$SYMBOL_REGEX" -exec cp -r {} $SYMBOLS_TEMP \;
|
||||
# cd $SYMBOLS_TEMP
|
||||
# $ZIP_COMMAND $ZIP_ARGS ../${{ steps.buildenv1.outputs.symbols_archive }} .
|
||||
#- name: Upload symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# curl --data-binary @${{ steps.buildenv1.outputs.symbols_archive }} "$CMAKE_BACKTRACE_URL/post?format=symbols&token=$CMAKE_BACKTRACE_SYMBOLS_TOKEN&upload_file=${{steps.buildenv1.outputs.symbols_archive}}&tag=$BUILD_NUMBER"
|
||||
#- name: Debug list symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# shell: bash
|
||||
# run: |
|
||||
# unzip -v "${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}"
|
||||
#- name: Upload debug list symbols
|
||||
# if: startsWith(matrix.os, 'windows') || startsWith(matrix.os, 'macOS')
|
||||
# uses: actions/upload-artifact@v1
|
||||
# with:
|
||||
# name: symbols
|
||||
# path: ${{runner.workspace}}/${{ steps.buildenv1.outputs.symbols_archive }}
|
||||
|
|
|
@ -112,11 +112,12 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::MixedAudio, PacketType::SilentAudioFrame },
|
||||
this, "handleAudioPacket");
|
||||
PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleAudioPacket));
|
||||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
PacketReceiver::makeSourcedListenerReference<Agent>(this, &Agent::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<Agent>(this, &Agent::handleSelectedAudioFormat));
|
||||
|
||||
// 100Hz timer for audio
|
||||
const int TARGET_INTERVAL_MSEC = 10; // 10ms
|
||||
|
|
|
@ -118,8 +118,10 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
setUpStatusToMonitor();
|
||||
}
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket");
|
||||
packetReceiver.registerListener(PacketType::CreateAssignment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
|
||||
packetReceiver.registerListener(PacketType::StopNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleStopNodePacket));
|
||||
}
|
||||
|
||||
void AssignmentClient::stopAssignmentClient() {
|
||||
|
|
|
@ -72,7 +72,8 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClientMonitor>(this, &AssignmentClientMonitor::handleChildStatusPacket));
|
||||
|
||||
adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
|
|
|
@ -308,7 +308,8 @@ AssetServer::AssetServer(ReceivedMessage& message) :
|
|||
|
||||
// Queue all requests until the Asset Server is fully setup
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::AssetGet, PacketType::AssetGetInfo, PacketType::AssetUpload, PacketType::AssetMappingOperation }, this, "queueRequests");
|
||||
packetReceiver.registerListenerForTypes({ PacketType::AssetGet, PacketType::AssetGetInfo, PacketType::AssetUpload, PacketType::AssetMappingOperation },
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::queueRequests));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
updateConsumedCores();
|
||||
|
@ -464,10 +465,14 @@ void AssetServer::completeSetup() {
|
|||
qCDebug(asset_server) << "Overriding temporary queuing packet handler.";
|
||||
// We're fully setup, override the request queueing handler and replay all requests
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
|
||||
packetReceiver.registerListener(PacketType::AssetUpload, this, "handleAssetUpload");
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperation, this, "handleAssetMappingOperation");
|
||||
packetReceiver.registerListener(PacketType::AssetGet,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetGet));
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfo,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetGetInfo));
|
||||
packetReceiver.registerListener(PacketType::AssetUpload,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetUpload));
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperation,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetServer>(this, &AssetServer::handleAssetMappingOperation));
|
||||
|
||||
replayRequests();
|
||||
}
|
||||
|
|
|
@ -101,20 +101,23 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
PacketType::InjectorGainSet,
|
||||
PacketType::AudioSoloRequest,
|
||||
PacketType::StopInjector },
|
||||
this, "queueAudioPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::queueAudioPacket)
|
||||
);
|
||||
|
||||
// packets whose consequences are global should be processed on the main thread
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleMuteEnvironmentPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeMuteRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleNodeMuteRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleKillAvatarPacket));
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedMicrophoneAudioNoEcho,
|
||||
PacketType::ReplicatedMicrophoneAudioWithEcho,
|
||||
PacketType::ReplicatedInjectAudio,
|
||||
PacketType::ReplicatedSilentAudioFrame
|
||||
},
|
||||
this, "queueReplicatedAudioPacket"
|
||||
PacketType::ReplicatedSilentAudioFrame },
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioMixer>(this, &AudioMixer::queueReplicatedAudioPacket)
|
||||
);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
|
|
|
@ -71,26 +71,38 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
|
||||
packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
|
||||
packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck, this, "queueIncomingPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListener(PacketType::AdjustAvatarSorting,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAdjustAvatarSorting));
|
||||
packetReceiver.registerListener(PacketType::AvatarQuery,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarQueryPacket));
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleAvatarIdentityPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleKillAvatarPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleNodeIgnoreRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleRadiusIgnoreRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::RequestsDomainListData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleRequestsDomainListDataPacket));
|
||||
packetReceiver.registerListener(PacketType::SetAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraitsAck,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "queueIncomingPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarMixer>(this, &AvatarMixer::queueIncomingPacket));
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedAvatarIdentity,
|
||||
PacketType::ReplicatedKillAvatar
|
||||
}, this, "handleReplicatedPacket");
|
||||
}, PacketReceiver::makeUnsourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleReplicatedPacket));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ReplicatedBulkAvatarData, this, "handleReplicatedBulkAvatarPacket");
|
||||
packetReceiver.registerListener(PacketType::ReplicatedBulkAvatarData,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AvatarMixer>(this, &AvatarMixer::handleReplicatedBulkAvatarPacket));
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &AvatarMixer::handlePacketVersionMismatch);
|
||||
|
|
|
@ -59,8 +59,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
|
|||
PacketType::ChallengeOwnership,
|
||||
PacketType::ChallengeOwnershipRequest,
|
||||
PacketType::ChallengeOwnershipReply },
|
||||
this,
|
||||
"handleEntityPacket");
|
||||
PacketReceiver::makeSourcedListenerReference<EntityServer>(this, &EntityServer::handleEntityPacket));
|
||||
|
||||
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
|
||||
_dynamicDomainVerificationTimer.setSingleShot(true);
|
||||
|
|
|
@ -25,9 +25,12 @@ MessagesMixer::MessagesMixer(ReceivedMessage& message) : ThreadedAssignment(mess
|
|||
{
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
packetReceiver.registerListener(PacketType::MessagesData,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessages));
|
||||
packetReceiver.registerListener(PacketType::MessagesSubscribe,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessagesSubscribe));
|
||||
packetReceiver.registerListener(PacketType::MessagesUnsubscribe,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesMixer>(this, &MessagesMixer::handleMessagesUnsubscribe));
|
||||
}
|
||||
|
||||
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
|
|
|
@ -1122,8 +1122,10 @@ void OctreeServer::run() {
|
|||
|
||||
void OctreeServer::domainSettingsRequestComplete() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack,
|
||||
PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeDataNackPacket));
|
||||
packetReceiver.registerListener(getMyQueryMessageType(),
|
||||
PacketReceiver::makeSourcedListenerReference<OctreeServer>(this, &OctreeServer::handleOctreeQueryPacket));
|
||||
|
||||
qDebug(octree_server) << "Received domain settings";
|
||||
|
||||
|
|
|
@ -83,13 +83,18 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleOctreePacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleSelectedAudioFormat));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleReloadEntityServerScriptPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityScriptGetStatusPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityServerScriptLogPacket));
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServer>(this, &EntityScriptServer::handleEntityScriptCallMethodPacket));
|
||||
|
||||
static const int LOG_INTERVAL = MSECS_PER_SECOND / 10;
|
||||
auto timer = new QTimer(this);
|
||||
|
|
|
@ -202,9 +202,9 @@
|
|||
; The Inner invocation has written an uninstaller binary for us.
|
||||
; We need to sign it if it's a production or PR build.
|
||||
!if @PRODUCTION_BUILD@ == 1
|
||||
!if @BYPASS_SIGNING@ == 1
|
||||
!if @BYPASS_SIGNING@ == TRUE
|
||||
!warning "BYPASS_SIGNING set - installer will not be signed"
|
||||
!else
|
||||
!else
|
||||
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://timestamp.comodoca.com?td=sha256 /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
|
||||
!endif
|
||||
!endif
|
||||
|
|
|
@ -500,6 +500,31 @@ function prepareAccessTokenPrompt(callback) {
|
|||
});
|
||||
}
|
||||
|
||||
function createDomainIDPrompt(callback) {
|
||||
swal({
|
||||
title: 'Finish Registering Domain',
|
||||
type: 'input',
|
||||
text: 'Enter a label for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>This is a required step for registration.</br></br>',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
html: true
|
||||
}, function (inputValue) {
|
||||
if (inputValue === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("Please enter a valid label for your machine.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(inputValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMetaverseUrl(callback) {
|
||||
$.ajax('/api/metaverse_info', {
|
||||
success: function(data) {
|
||||
|
|
|
@ -40,6 +40,8 @@ $(document).ready(function(){
|
|||
|
||||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
// hide the places table for now because we do not want that interacted with from the domain-server
|
||||
$('#' + Settings.PLACES_TABLE_ID).hide();
|
||||
|
||||
setupDomainNetworkingSettings();
|
||||
// setupDomainLabelSetting();
|
||||
|
@ -363,7 +365,7 @@ $(document).ready(function(){
|
|||
confirmButtonText: "Create",
|
||||
closeOnConfirm: false,
|
||||
html: true
|
||||
}, function(inputValue){
|
||||
}, function (inputValue) {
|
||||
if (inputValue === false) {
|
||||
swal.close();
|
||||
|
||||
|
@ -373,7 +375,7 @@ $(document).ready(function(){
|
|||
}
|
||||
} else {
|
||||
// we're going to change the alert to a new one with a spinner while we create this domain
|
||||
showSpinnerAlert('Creating domain ID');
|
||||
// showSpinnerAlert('Creating domain ID');
|
||||
createNewDomainID(inputValue, justConnected);
|
||||
}
|
||||
});
|
||||
|
@ -385,7 +387,12 @@ $(document).ready(function(){
|
|||
"label": label
|
||||
}
|
||||
|
||||
$.post("/api/domains", domainJSON, function(data){
|
||||
$.post("/api/domains", domainJSON, function(data) {
|
||||
if (data.status === "failure") {
|
||||
failedToCreateDomainID(data, justConnected);
|
||||
return;
|
||||
}
|
||||
|
||||
// we successfully created a domain ID, set it on that field
|
||||
var domainID = data.domain.domainId;
|
||||
console.log("Setting domain id to ", data, domainID);
|
||||
|
@ -406,40 +413,50 @@ $(document).ready(function(){
|
|||
text: successText,
|
||||
html: true,
|
||||
confirmButtonText: 'Save'
|
||||
}, function(){
|
||||
}, function () {
|
||||
saveSettings();
|
||||
});
|
||||
}, 'json').fail(function(){
|
||||
}, 'json').fail(function (data) {
|
||||
failedToCreateDomainID(data, justConnected);
|
||||
});
|
||||
}
|
||||
|
||||
function failedToCreateDomainID(data, justConnected) {
|
||||
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
|
||||
|
||||
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
|
||||
if (data && data.status === "failure") {
|
||||
errorText = "Error: " + data.error + "</br>Do you want to try again or";
|
||||
console.log("Error: " + data.error);
|
||||
} else {
|
||||
console.log("Error: Failed to post to metaverse.");
|
||||
}
|
||||
|
||||
if (justConnected) {
|
||||
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
|
||||
if (justConnected) {
|
||||
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
|
||||
} else {
|
||||
errorText += " cancel?"
|
||||
}
|
||||
|
||||
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
|
||||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: errorText,
|
||||
html: true,
|
||||
confirmButtonText: 'Try again',
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false
|
||||
}, function (isConfirm) {
|
||||
if (isConfirm) {
|
||||
// they want to try creating a domain ID again
|
||||
showDomainCreationAlert(justConnected);
|
||||
} else {
|
||||
errorText += " cancel?"
|
||||
}
|
||||
|
||||
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
|
||||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: errorText,
|
||||
html: true,
|
||||
confirmButtonText: 'Try again',
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false
|
||||
}, function(isConfirm){
|
||||
if (isConfirm) {
|
||||
// they want to try creating a domain ID again
|
||||
showDomainCreationAlert(justConnected);
|
||||
} else {
|
||||
// they want to cancel
|
||||
if (justConnected) {
|
||||
// since they just connected we need to save the access token here
|
||||
saveSettings();
|
||||
}
|
||||
// they want to cancel
|
||||
if (justConnected) {
|
||||
// since they just connected we need to save the access token here
|
||||
saveSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -711,8 +728,8 @@ $(document).ready(function(){
|
|||
name: 'places',
|
||||
label: 'Places',
|
||||
html_id: Settings.PLACES_TABLE_ID,
|
||||
help: "The following places currently point to this domain.</br>To point places to this domain, "
|
||||
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>My Places</a> "
|
||||
help: "To point places to this domain, "
|
||||
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>Places</a> "
|
||||
+ "page in your Metaverse account.",
|
||||
read_only: true,
|
||||
can_add_new_rows: false,
|
||||
|
@ -745,9 +762,10 @@ $(document).ready(function(){
|
|||
var errorEl = createDomainLoadingError("There was an error retrieving your places.");
|
||||
$("#" + Settings.PLACES_TABLE_ID).after(errorEl);
|
||||
|
||||
var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
temporaryPlaceButton.hide();
|
||||
$('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
// DISABLE TEMP PLACE NAME BUTTON...
|
||||
// var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
|
||||
// temporaryPlaceButton.hide();
|
||||
// $('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
|
||||
if (accessTokenIsSet()) {
|
||||
appendAddButtonToPlacesTable();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step col-md-8 col-centered" style="display: none;">
|
||||
<!-- <div class="wizard-step col-md-8 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
@ -51,7 +51,7 @@
|
|||
<button type="button" class="btn btn-md btn-block btn-primary next-button">Next</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="wizard-step col-md-9 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
|
@ -204,7 +204,7 @@
|
|||
</div>
|
||||
<br />
|
||||
<div id="automatic-threading-options-row" class="row">
|
||||
<p class="col-md-2">
|
||||
<p class="col-md-12">
|
||||
<label>
|
||||
<input id="enable-automatic-threading" name="threading-checkbox" type="checkbox" value="true"> Enable Automatic Threading
|
||||
</label>
|
||||
|
|
|
@ -50,6 +50,7 @@ $(document).ready(function(){
|
|||
prepareAccessTokenPrompt(function(accessToken) {
|
||||
Metaverse.accessToken = accessToken;
|
||||
saveAccessToken();
|
||||
promptToCreateDomainID();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -171,6 +172,45 @@ function setupWizardSteps() {
|
|||
$(currentStep).show();
|
||||
}
|
||||
|
||||
function promptToCreateDomainID() {
|
||||
setTimeout(function () {
|
||||
createDomainIDPrompt(function (label) {
|
||||
var domainJSON = {
|
||||
"label": label
|
||||
};
|
||||
|
||||
$.post("/api/domains", domainJSON, function (data) {
|
||||
if (data.status === "failure") {
|
||||
swal.showInputError("Error: " + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
swal.close();
|
||||
|
||||
// we successfully created a domain ID, set it on that field
|
||||
var domainID = data.domain.domainId;
|
||||
console.log("Setting domain ID to ", data, domainID);
|
||||
|
||||
var formJSON = {
|
||||
"metaverse": {
|
||||
"id": domainID
|
||||
}
|
||||
};
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, goToNextStep);
|
||||
}, 'json').fail(function (data) {
|
||||
if (data && data.status === "failure") {
|
||||
swal.showInputError("Error: " + data.error);
|
||||
} else {
|
||||
swal.showInputError("Error: Failed to post to metaverse.");
|
||||
}
|
||||
console.log("Failed to create domain ID...");
|
||||
});
|
||||
});
|
||||
}, 500); // Apparently swal needs time before opening another prompt.
|
||||
}
|
||||
|
||||
function updatePlaceNameLink(address) {
|
||||
if (address) {
|
||||
var url = URLs.PLACE_URL + '/' + address;
|
||||
|
@ -341,7 +381,7 @@ function saveAccessToken() {
|
|||
$(this).blur();
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, goToNextStep);
|
||||
postSettings(formJSON);
|
||||
}
|
||||
|
||||
function getSettingDescriptionForKey(groupKey, settingKey) {
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include <openssl/x509.h>
|
||||
#include <random>
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QMetaMethod>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
|
|
|
@ -778,32 +778,51 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
|
||||
// register as the packet receiver for the types we want
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacket");
|
||||
packetReceiver.registerListener(PacketType::RequestAssignment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processRequestAssignmentPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainListRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServer>(this, &DomainServer::processListRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processPathQueryPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeJsonStats,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServer>(this, &DomainServer::processNodeJSONStatsPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processNodeDisconnectRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processAvatarZonePresencePacket));
|
||||
|
||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::NodeKickRequest, &_settingsManager, "processNodeKickRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDRequest, &_settingsManager, "processUsernameFromIDRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processSettingsRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::NodeKickRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processNodeKickRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<DomainServerSettingsManager>(&_settingsManager, &DomainServerSettingsManager::processUsernameFromIDRequestPacket));
|
||||
|
||||
// register the gatekeeper for the packets it needs to receive
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainConnectRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processConnectRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPing,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPingPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainGatekeeper>(&_gatekeeper, &DomainGatekeeper::processICEPeerInformationPacket));
|
||||
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied, this, "processICEServerHeartbeatDenialPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK, this, "processICEServerHeartbeatACK");
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatDenied,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processICEServerHeartbeatDenialPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEServerHeartbeatACK,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processICEServerHeartbeatACK));
|
||||
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileRequest, this, "processOctreeDataRequestMessage");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileRequest,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processOctreeDataRequestMessage));
|
||||
packetReceiver.registerListener(PacketType::OctreeDataPersist,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::processOctreeDataPersistMessage));
|
||||
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacementRequest");
|
||||
packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl, this, "handleDomainContentReplacementFromURLRequest");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::handleOctreeFileReplacementRequest));
|
||||
packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this, &DomainServer::handleDomainContentReplacementFromURLRequest));
|
||||
|
||||
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
|
||||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
|
|
|
@ -212,6 +212,8 @@ private:
|
|||
|
||||
/// guard read/write access from multiple threads to settings
|
||||
QReadWriteLock _settingsLock { QReadWriteLock::Recursive };
|
||||
|
||||
friend class DomainServer;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServerSettingsManager_h
|
||||
|
|
Binary file not shown.
|
@ -333,6 +333,8 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: childrenRect.height;
|
||||
// FIXME: Reuse or remove wallet-related code.
|
||||
visible: false;
|
||||
|
||||
Rectangle {
|
||||
id: walletHeaderContainer;
|
||||
|
|
|
@ -288,8 +288,10 @@ Wallet::Wallet() {
|
|||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
_passphrase = new QString("");
|
||||
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnership,
|
||||
PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest,
|
||||
PacketReceiver::makeSourcedListenerReference<Wallet>(this, &Wallet::handleChallengeOwnershipPacket));
|
||||
|
||||
connect(ledger.data(), &Ledger::accountResult, this, [](QJsonObject result) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
|
|
|
@ -25,7 +25,8 @@ OctreePacketProcessor::OctreePacketProcessor():
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
const PacketReceiver::PacketTypeList octreePackets =
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete };
|
||||
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket");
|
||||
packetReceiver.registerDirectListenerForTypes(octreePackets,
|
||||
PacketReceiver::makeSourcedListenerReference<OctreePacketProcessor>(this, &OctreePacketProcessor::handleOctreePacket));
|
||||
}
|
||||
|
||||
OctreePacketProcessor::~OctreePacketProcessor() { }
|
||||
|
|
|
@ -44,7 +44,8 @@ ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
|
|||
// This packet listener handles the packet containing information about the latest zone ID in which we are allowed to share.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence, this, "processAvatarZonePresencePacketOnClient");
|
||||
packetReceiver.registerListener(PacketType::AvatarZonePresence,
|
||||
PacketReceiver::makeUnsourcedListenerReference<ScreenshareScriptingInterface>(this, &ScreenshareScriptingInterface::processAvatarZonePresencePacketOnClient));
|
||||
};
|
||||
|
||||
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
//
|
||||
|
||||
#include "DomainConnectionModel.h"
|
||||
#include <QLoggingCategory>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QMetaMethod>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
|
|
@ -81,7 +81,8 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply,
|
||||
PacketReceiver::makeSourcedListenerReference<ContextOverlayInterface>(this, &ContextOverlayInterface::handleChallengeOwnershipReplyPacket));
|
||||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -369,13 +369,20 @@ AudioClient::AudioClient() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::AudioEnvironment, this, "handleAudioEnvironmentDataPacket");
|
||||
packetReceiver.registerListener(PacketType::SilentAudioFrame, this, "handleAudioDataPacket");
|
||||
packetReceiver.registerListener(PacketType::MixedAudio, this, "handleAudioDataPacket");
|
||||
packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket");
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioIOStats>(&_stats, &AudioIOStats::processStreamStatsPacket));
|
||||
packetReceiver.registerListener(PacketType::AudioEnvironment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioEnvironmentDataPacket));
|
||||
packetReceiver.registerListener(PacketType::SilentAudioFrame,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioDataPacket));
|
||||
packetReceiver.registerListener(PacketType::MixedAudio,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleAudioDataPacket));
|
||||
packetReceiver.registerListener(PacketType::NoisyMute,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleNoisyMutePacket));
|
||||
packetReceiver.registerListener(PacketType::MuteEnvironment,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleMuteEnvironmentPacket));
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AudioClient>(this, &AudioClient::handleSelectedAudioFormat));
|
||||
|
||||
auto& domainHandler = nodeList->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this] {
|
||||
|
|
|
@ -123,10 +123,14 @@ AvatarHashMap::AvatarHashMap() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits");
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarData,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processAvatarDataPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processKillAvatar));
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processAvatarIdentityPacket));
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<AvatarHashMap>(this, &AvatarHashMap::processBulkAvatarTraits));
|
||||
|
||||
connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) :
|
|||
}
|
||||
});
|
||||
|
||||
nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride");
|
||||
nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits,
|
||||
PacketReceiver::makeSourcedListenerReference<ClientTraitsHandler>(this, &ClientTraitsHandler::processTraitOverride));
|
||||
}
|
||||
|
||||
void ClientTraitsHandler::markTraitUpdated(AvatarTraits::TraitType updatedTrait) {
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
|
||||
EntityEditPacketSender::EntityEditPacketSender() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerDirectListener(PacketType::EntityEditNack, this, "processEntityEditNackPacket");
|
||||
packetReceiver.registerDirectListener(PacketType::EntityEditNack,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityEditPacketSender>(this, &EntityEditPacketSender::processEntityEditNackPacket));
|
||||
}
|
||||
|
||||
void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
EntityScriptServerLogClient::EntityScriptServerLogClient() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptServerLogClient>(this, &EntityScriptServerLogClient::handleEntityServerScriptLogPacket));
|
||||
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeActivated, this, &EntityScriptServerLogClient::nodeActivated);
|
||||
QObject::connect(nodeList.data(), &NodeList::nodeKilled, this, &EntityScriptServerLogClient::nodeKilled);
|
||||
|
|
|
@ -59,7 +59,8 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
|
|||
connect(nodeList.data(), &NodeList::canGetAndSetPrivateUserDataChanged, this, &EntityScriptingInterface::canGetAndSetPrivateUserDataChanged);
|
||||
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptCallMethod,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptingInterface>(this, &EntityScriptingInterface::handleEntityScriptCallMethodPacket));
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
|
||||
|
|
|
@ -74,6 +74,10 @@ static void* getGlProcessAddress(const char *namez) {
|
|||
|
||||
#else
|
||||
|
||||
|
||||
typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value);
|
||||
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC QueryCurrentRendererIntegerMESA;
|
||||
|
||||
static void* getGlProcessAddress(const char *namez) {
|
||||
return (void*)glXGetProcAddressARB((const GLubyte*)namez);
|
||||
}
|
||||
|
@ -92,6 +96,10 @@ void gl::initModuleGl() {
|
|||
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB");
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
QueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)getGlProcessAddress("glXQueryCurrentRendererIntegerMESA");
|
||||
#endif
|
||||
|
||||
#if defined(USE_GLES)
|
||||
gladLoadGLES2Loader(getGlProcessAddress);
|
||||
#else
|
||||
|
@ -124,3 +132,14 @@ void gl::setSwapInterval(int interval) {
|
|||
Q_UNUSED(interval);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool gl::queryCurrentRendererIntegerMESA(int attr, unsigned int *value) {
|
||||
#if defined(Q_OS_LINUX)
|
||||
if (QueryCurrentRendererIntegerMESA) {
|
||||
return QueryCurrentRendererIntegerMESA(attr, value);
|
||||
}
|
||||
#endif
|
||||
|
||||
*value = 0;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace gl {
|
|||
void initModuleGl();
|
||||
int getSwapInterval();
|
||||
void setSwapInterval(int swapInterval);
|
||||
bool queryCurrentRendererIntegerMESA(int attr, unsigned int *value);
|
||||
}
|
||||
|
||||
#endif // hifi_gpu_GPUConfig_h
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
//
|
||||
#include "GLBackend.h"
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include "gl/Config.h"
|
||||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
|
@ -105,13 +107,27 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
};
|
||||
|
||||
#define GL_GET_INTEGER(NAME) glGetIntegerv(GL_##NAME, &const_cast<GLint&>(NAME));
|
||||
|
||||
|
||||
#define BYTES_PER_KIB 1024L
|
||||
#define BYTES_PER_MIB (1024L * BYTES_PER_KIB)
|
||||
|
||||
GLint GLBackend::MAX_TEXTURE_IMAGE_UNITS{ 0 };
|
||||
GLint GLBackend::MAX_UNIFORM_BUFFER_BINDINGS{ 0 };
|
||||
GLint GLBackend::MAX_COMBINED_UNIFORM_BLOCKS{ 0 };
|
||||
GLint GLBackend::MAX_COMBINED_TEXTURE_IMAGE_UNITS{ 0 };
|
||||
GLint GLBackend::MAX_UNIFORM_BLOCK_SIZE{ 0 };
|
||||
GLint GLBackend::UNIFORM_BUFFER_OFFSET_ALIGNMENT{ 1 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX{ 0 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX{ 0 };
|
||||
GLint GLBackend::GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX{ 0 };
|
||||
GLint GLBackend::TEXTURE_FREE_MEMORY_ATI{ 0 };
|
||||
|
||||
size_t GLBackend::_totalMemory{ 0 };
|
||||
size_t GLBackend::_dedicatedMemory{ 0 };
|
||||
GLBackend::VideoCardType GLBackend::_videoCard{ GLBackend::Unknown };
|
||||
|
||||
|
||||
#define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
|
||||
|
||||
void GLBackend::init() {
|
||||
static std::once_flag once;
|
||||
|
@ -132,13 +148,59 @@ void GLBackend::init() {
|
|||
GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE);
|
||||
GL_GET_INTEGER(UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||
|
||||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
unsigned int mem;
|
||||
|
||||
if (vendor.contains("NVIDIA") ) {
|
||||
qCDebug(gpugllogging) << "NVIDIA card detected";
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
|
||||
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: " << GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: " << GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: " << GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
|
||||
|
||||
_totalMemory = GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX * BYTES_PER_KIB;
|
||||
_dedicatedMemory = GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX * BYTES_PER_KIB;
|
||||
_videoCard = NVIDIA;
|
||||
|
||||
|
||||
} else if (vendor.contains("ATI")) {
|
||||
qCDebug(gpugllogging) << "ATI card detected";
|
||||
GL_GET_INTEGER(TEXTURE_FREE_MEMORY_ATI);
|
||||
|
||||
_totalMemory = TEXTURE_FREE_MEMORY_ATI * BYTES_PER_KIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = ATI;
|
||||
} else if ( ::gl::queryCurrentRendererIntegerMESA(GLX_RENDERER_VIDEO_MEMORY_MESA, &mem) ) {
|
||||
// This works only on Linux. queryCurrentRendererIntegerMESA will return false if the
|
||||
// function is not supported because we're not on Linux, or for any other reason.
|
||||
qCDebug(gpugllogging) << "MESA card detected";
|
||||
_totalMemory = mem * BYTES_PER_MIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = MESA;
|
||||
} else {
|
||||
qCCritical(gpugllogging) << "Don't know how to get memory for OpenGL vendor " << vendor << "; renderer " << renderer << ", trying fallback";
|
||||
_videoCard = Unknown;
|
||||
_dedicatedMemory = gpu->getMemory();
|
||||
_totalMemory = _dedicatedMemory;
|
||||
}
|
||||
|
||||
qCDebug(gpugllogging) << "dedicated: " << _dedicatedMemory;
|
||||
qCDebug(gpugllogging) << "total: " << _totalMemory;
|
||||
|
||||
|
||||
LOG_GL_CONTEXT_INFO(gpugllogging, contextInfo);
|
||||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
|
||||
|
||||
// From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers.
|
||||
qCDebug(gpugllogging) << "GPU:";
|
||||
qCDebug(gpugllogging) << "\tcard:" << gpu->getName();
|
||||
qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver();
|
||||
qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB";
|
||||
qCDebug(gpugllogging) << "\ttotal memory:" << (_totalMemory / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "\tdedicated memory:" << (_dedicatedMemory / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "\tavailable memory:" << (getAvailableMemory() / BYTES_PER_KIB) << "KB";
|
||||
qCDebug(gpugllogging) << "Limits:";
|
||||
qCDebug(gpugllogging) << "\tmax textures:" << MAX_TEXTURE_IMAGE_UNITS;
|
||||
qCDebug(gpugllogging) << "\tmax texture binding:" << MAX_COMBINED_TEXTURE_IMAGE_UNITS;
|
||||
|
@ -152,6 +214,41 @@ void GLBackend::init() {
|
|||
});
|
||||
}
|
||||
|
||||
size_t GLBackend::getAvailableMemory() {
|
||||
GLint mem;
|
||||
|
||||
switch( _videoCard ) {
|
||||
case NVIDIA:
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
case ATI:
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &mem);
|
||||
return mem * BYTES_PER_KIB;
|
||||
case MESA:
|
||||
return 0; // Don't know the current value
|
||||
case Unknown:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
bool GLBackend::availableMemoryKnown() {
|
||||
switch( _videoCard ) {
|
||||
case NVIDIA:
|
||||
return true;
|
||||
case ATI:
|
||||
return true;
|
||||
case MESA:
|
||||
return false;
|
||||
case Unknown:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GLBackend::GLBackend(bool syncCache) {
|
||||
_pipeline._cameraCorrectionBuffer._buffer->flush();
|
||||
initShaderBinaryCache();
|
||||
|
|
|
@ -67,6 +67,13 @@ protected:
|
|||
GLBackend();
|
||||
|
||||
public:
|
||||
enum VideoCardType {
|
||||
ATI,
|
||||
NVIDIA,
|
||||
MESA,
|
||||
Unknown
|
||||
};
|
||||
|
||||
#if defined(USE_GLES)
|
||||
// https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGet.xhtml
|
||||
static const GLint MIN_REQUIRED_TEXTURE_IMAGE_UNITS = 16;
|
||||
|
@ -89,6 +96,25 @@ public:
|
|||
static GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS;
|
||||
static GLint MAX_UNIFORM_BLOCK_SIZE;
|
||||
static GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT;
|
||||
static GLint GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
static GLint GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
static GLint GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
|
||||
static GLint TEXTURE_FREE_MEMORY_ATI;
|
||||
|
||||
|
||||
static size_t _totalMemory;
|
||||
static size_t _dedicatedMemory;
|
||||
static VideoCardType _videoCard;
|
||||
|
||||
|
||||
static size_t getTotalMemory() { return _totalMemory; }
|
||||
static size_t getDedicatedMemory() { return _dedicatedMemory; }
|
||||
|
||||
static size_t getAvailableMemory();
|
||||
static bool availableMemoryKnown();
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ~GLBackend();
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2
|
||||
#define NO_BUFFER_WORK_SLEEP_TIME_MS 2
|
||||
#define THREADED_TEXTURE_BUFFERING 1
|
||||
#define MAX_AUTO_FRACTION_OF_TOTAL_MEMORY 0.8f
|
||||
#define AUTO_RESERVE_TEXTURE_MEMORY MB_TO_BYTES(64)
|
||||
|
||||
static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB);
|
||||
|
||||
|
@ -183,9 +185,25 @@ void GLTextureTransferEngineDefault::manageMemory() {
|
|||
void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
||||
|
||||
bool useAvailableGlMemory = false;
|
||||
size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
|
||||
|
||||
if (0 == allowedMemoryAllocation) {
|
||||
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
|
||||
// Automatic allocation
|
||||
|
||||
if (GLBackend::availableMemoryKnown()) {
|
||||
// If we know how much is free, then we use that
|
||||
useAvailableGlMemory = true;
|
||||
} else {
|
||||
// We don't know how much is free, so leave some reasonable spare room
|
||||
// and hope it works.
|
||||
allowedMemoryAllocation = GLBackend::getTotalMemory() * MAX_AUTO_FRACTION_OF_TOTAL_MEMORY;
|
||||
|
||||
if (0 == allowedMemoryAllocation) {
|
||||
// Last resort, if we failed to detect
|
||||
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any defunct textures (weak pointers that no longer have a valid texture)
|
||||
|
@ -205,7 +223,7 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
|||
idealMemoryAllocation += texture->evalTotalSize();
|
||||
// Track how much we're actually using
|
||||
totalVariableMemoryAllocation += gltexture->size();
|
||||
if (vartexture->canDemote()) {
|
||||
if (!gltexture->_gpuObject.getImportant() && vartexture->canDemote()) {
|
||||
canDemote |= true;
|
||||
}
|
||||
if (vartexture->canPromote()) {
|
||||
|
@ -218,7 +236,22 @@ void GLTextureTransferEngineDefault::updateMemoryPressure() {
|
|||
|
||||
Backend::textureResourceIdealGPUMemSize.set(idealMemoryAllocation);
|
||||
size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation;
|
||||
float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
|
||||
float pressure = 0;
|
||||
|
||||
if (useAvailableGlMemory) {
|
||||
size_t totalMem = GLBackend::getTotalMemory();
|
||||
size_t availMem = GLBackend::getAvailableMemory();
|
||||
|
||||
if (availMem >= AUTO_RESERVE_TEXTURE_MEMORY) {
|
||||
availMem -= AUTO_RESERVE_TEXTURE_MEMORY;
|
||||
} else {
|
||||
availMem = 0;
|
||||
}
|
||||
|
||||
pressure = ((float)totalMem - (float)availMem) / (float)totalMem;
|
||||
} else {
|
||||
pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
|
||||
}
|
||||
|
||||
// If we're oversubscribed we need to demote textures IMMEDIATELY
|
||||
if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) {
|
||||
|
@ -470,7 +503,7 @@ void GLTextureTransferEngineDefault::processDemotes(size_t reliefRequired, const
|
|||
for (const auto& texture : strongTextures) {
|
||||
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
|
||||
if (vargltexture->canDemote()) {
|
||||
if (!gltexture->_gpuObject.getImportant() && vargltexture->canDemote()) {
|
||||
demoteQueue.push({ texture, (float)gltexture->size() });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -571,6 +571,9 @@ public:
|
|||
void setExternalRecycler(const ExternalRecycler& recycler);
|
||||
ExternalRecycler getExternalRecycler() const;
|
||||
|
||||
bool getImportant() const { return _important; }
|
||||
void setImportant(bool important) { _important = important; }
|
||||
|
||||
const GPUObjectPointer gpuObject {};
|
||||
|
||||
ExternalUpdates getUpdates() const;
|
||||
|
@ -632,6 +635,7 @@ protected:
|
|||
bool _autoGenerateMips = false;
|
||||
bool _isIrradianceValid = false;
|
||||
bool _defined = false;
|
||||
bool _important = false;
|
||||
|
||||
static TexturePointer create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler);
|
||||
|
||||
|
|
|
@ -43,10 +43,14 @@ AssetClient::AssetClient() {
|
|||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperationReply, this, "handleAssetMappingOperationReply");
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfoReply, this, "handleAssetGetInfoReply");
|
||||
packetReceiver.registerListener(PacketType::AssetGetReply, this, "handleAssetGetReply", true);
|
||||
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
|
||||
packetReceiver.registerListener(PacketType::AssetMappingOperationReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetMappingOperationReply));
|
||||
packetReceiver.registerListener(PacketType::AssetGetInfoReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetGetInfoReply));
|
||||
packetReceiver.registerListener(PacketType::AssetGetReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetGetReply), true);
|
||||
packetReceiver.registerListener(PacketType::AssetUploadReply,
|
||||
PacketReceiver::makeSourcedListenerReference<AssetClient>(this, &AssetClient::handleAssetUploadReply));
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled);
|
||||
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
|
||||
|
|
|
@ -76,9 +76,9 @@ const quint16 DOMAIN_SERVER_EXPORTER_PORT =
|
|||
|
||||
const quint16 DOMAIN_SERVER_METADATA_EXPORTER_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
.contains("DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.contains("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.value("VIRCADIA_DOMAIN_SERVER_METADATA_EXPORTER_PORT")
|
||||
.toUInt()
|
||||
: 9704;
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ EntityScriptClient::EntityScriptClient() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply, this, "handleGetScriptStatusReply");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply,
|
||||
PacketReceiver::makeSourcedListenerReference<EntityScriptClient>(this, &EntityScriptClient::handleGetScriptStatusReply));
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptClient::handleNodeKilled);
|
||||
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileSelector>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <StatTracker.h>
|
||||
|
@ -54,6 +55,7 @@ void FileResourceRequest::doSend() {
|
|||
} else {
|
||||
QFile file(filename);
|
||||
if (file.exists()) {
|
||||
setProperty("last-modified", toHttpDateString(QFileInfo(file).lastModified().toMSecsSinceEpoch()));
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
|
||||
if (file.size() < _byteRange.fromInclusive || file.size() < _byteRange.toExclusive) {
|
||||
|
|
|
@ -28,7 +28,8 @@ MessagesClient::MessagesClient() {
|
|||
});
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket");
|
||||
packetReceiver.registerListener(PacketType::MessagesData,
|
||||
PacketReceiver::makeSourcedListenerReference<MessagesClient>(this, &MessagesClient::handleMessagesPacket));
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
|
||||
}
|
||||
|
||||
|
|
|
@ -139,20 +139,34 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
|
|||
startSTUNPublicSocketUpdate();
|
||||
|
||||
auto& packetReceiver = getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::DomainList, this, "processDomainServerList");
|
||||
packetReceiver.registerListener(PacketType::Ping, this, "processPingPacket");
|
||||
packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode");
|
||||
packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainConnectionDenied, &_domainHandler, "processDomainServerConnectionDeniedPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainSettings, &_domainHandler, "processSettingsPacketList");
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse");
|
||||
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode");
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDReply, this, "processUsernameFromIDReply");
|
||||
packetReceiver.registerListener(PacketType::DomainList,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerList));
|
||||
packetReceiver.registerListener(PacketType::Ping,
|
||||
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingPacket));
|
||||
packetReceiver.registerListener(PacketType::PingReply,
|
||||
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPing,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processICEPingPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerAddedNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerAddedNode));
|
||||
packetReceiver.registerListener(PacketType::DomainServerConnectionToken,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerConnectionTokenPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainConnectionDenied,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processDomainServerConnectionDeniedPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainSettings,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processSettingsPacketList));
|
||||
packetReceiver.registerListener(PacketType::ICEServerPeerInformation,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processICEResponsePacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processDTLSRequirementPacket));
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainHandler>(&_domainHandler, &DomainHandler::processICEPingReplyPacket));
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathResponse,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerPathResponse));
|
||||
packetReceiver.registerListener(PacketType::DomainServerRemovedNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerRemovedNode));
|
||||
packetReceiver.registerListener(PacketType::UsernameFromIDReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processUsernameFromIDReply));
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStats(QJsonObject statsObject, HifiSockAddr destination) {
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
#include "PacketReceiver.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtCore/QMutexLocker>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
@ -25,85 +26,57 @@ PacketReceiver::PacketReceiver(QObject* parent) : QObject(parent) {
|
|||
qRegisterMetaType<QSharedPointer<ReceivedMessage>>();
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListenerForTypes(PacketTypeList types, QObject* listener, const char* slot) {
|
||||
bool PacketReceiver::ListenerReference::invokeWithQt(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) {
|
||||
ListenerReferencePointer thisPointer = sharedFromThis();
|
||||
return QMetaObject::invokeMethod(getObject(), [=]() {
|
||||
thisPointer->invokeDirectly(receivedMessagePointer, sourceNode);
|
||||
});
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(!types.empty(), "PacketReceiver::registerListenerForTypes", "No types to register");
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerListenerForTypes", "No slot to register");
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListenerForTypes", "No listener to register");
|
||||
|
||||
// Partition types based on whether they are sourced or not (non sourced in front)
|
||||
auto middle = std::partition(std::begin(types), std::end(types), [](PacketType type) {
|
||||
return PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||
});
|
||||
|
||||
QMetaMethod nonSourcedMethod, sourcedMethod;
|
||||
|
||||
// Check we have a valid method for non sourced types if any
|
||||
if (middle != std::begin(types)) {
|
||||
nonSourcedMethod = matchingMethodForListener(*std::begin(types), listener, slot);
|
||||
if (!nonSourcedMethod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have a valid method for sourced types if any
|
||||
if (middle != std::end(types)) {
|
||||
sourcedMethod = matchingMethodForListener(*middle, listener, slot);
|
||||
if (!sourcedMethod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Register non sourced types
|
||||
std::for_each(std::begin(types), middle, [this, &listener, &nonSourcedMethod](PacketType type) {
|
||||
registerVerifiedListener(type, listener, nonSourcedMethod);
|
||||
});
|
||||
|
||||
// Register sourced types
|
||||
std::for_each(middle, std::end(types), [this, &listener, &sourcedMethod](PacketType type) {
|
||||
registerVerifiedListener(type, listener, sourcedMethod);
|
||||
std::for_each(std::begin(types), std::end(types), [this, &listener](PacketType type) {
|
||||
registerVerifiedListener(type, listener);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketReceiver::registerDirectListener(PacketType type, QObject* listener, const char* slot) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListener", "No slot to register");
|
||||
void PacketReceiver::registerDirectListener(PacketType type, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListener", "No listener to register");
|
||||
|
||||
bool success = registerListener(type, listener, slot);
|
||||
bool success = registerListener(type, listener);
|
||||
if (success) {
|
||||
QMutexLocker locker(&_directConnectSetMutex);
|
||||
|
||||
// if we successfully registered, add this object to the set of objects that are directly connected
|
||||
_directlyConnectedObjects.insert(listener);
|
||||
_directlyConnectedObjects.insert(listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types,
|
||||
QObject* listener, const char* slot) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerDirectListenerForTypes", "No slot to register");
|
||||
void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerDirectListenerForTypes", "No listener to register");
|
||||
|
||||
// just call register listener for types to start
|
||||
bool success = registerListenerForTypes(std::move(types), listener, slot);
|
||||
bool success = registerListenerForTypes(std::move(types), listener);
|
||||
if (success) {
|
||||
QMutexLocker locker(&_directConnectSetMutex);
|
||||
|
||||
// if we successfully registered, add this object to the set of objects that are directly connected
|
||||
_directlyConnectedObjects.insert(listener);
|
||||
_directlyConnectedObjects.insert(listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
bool PacketReceiver::registerListener(PacketType type, QObject* listener, const char* slot,
|
||||
bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No object to register");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::registerListener", "No slot to register");
|
||||
bool PacketReceiver::registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerListener", "No listener to register");
|
||||
|
||||
QMetaMethod matchingMethod = matchingMethodForListener(type, listener, slot);
|
||||
bool matchingMethod = matchingMethodForListener(type, listener);
|
||||
|
||||
if (matchingMethod.isValid()) {
|
||||
if (matchingMethod) {
|
||||
qCDebug(networking) << "Registering a packet listener for packet list type" << type;
|
||||
registerVerifiedListener(type, listener, matchingMethod, deliverPending);
|
||||
registerVerifiedListener(type, listener, deliverPending);
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type;
|
||||
|
@ -111,62 +84,23 @@ bool PacketReceiver::registerListener(PacketType type, QObject* listener, const
|
|||
}
|
||||
}
|
||||
|
||||
QMetaMethod PacketReceiver::matchingMethodForListener(PacketType type, QObject* object, const char* slot) const {
|
||||
Q_ASSERT_X(object, "PacketReceiver::matchingMethodForListener", "No object to call");
|
||||
Q_ASSERT_X(slot, "PacketReceiver::matchingMethodForListener", "No slot to call");
|
||||
bool PacketReceiver::matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::matchingMethodForListener", "No listener to call");
|
||||
|
||||
// normalize the slot with the expected parameters
|
||||
static const QString SIGNATURE_TEMPLATE("%1(%2)");
|
||||
static const QString NON_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>";
|
||||
bool isSourced = listener->isSourced();
|
||||
bool isNonSourcedPacket = PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||
|
||||
QSet<QString> possibleSignatures {
|
||||
SIGNATURE_TEMPLATE.arg(slot, NON_SOURCED_MESSAGE_LISTENER_PARAMETERS)
|
||||
};
|
||||
|
||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(type)) {
|
||||
static const QString SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>,QSharedPointer<Node>";
|
||||
static const QString TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer<ReceivedMessage>,SharedNodePointer";
|
||||
|
||||
// a sourced packet must take the shared pointer to the ReceivedMessage but optionally could include
|
||||
// a shared pointer to the node
|
||||
possibleSignatures << SIGNATURE_TEMPLATE.arg(slot, TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS);
|
||||
possibleSignatures << SIGNATURE_TEMPLATE.arg(slot, SOURCED_MESSAGE_LISTENER_PARAMETERS);
|
||||
assert(!isSourced || !isNonSourcedPacket);
|
||||
if (isSourced && isNonSourcedPacket) {
|
||||
qCDebug(networking) << "PacketReceiver::registerListener cannot support a sourced listener for type" << type;
|
||||
return false;
|
||||
}
|
||||
|
||||
int methodIndex = -1;
|
||||
|
||||
foreach(const QString& signature, possibleSignatures) {
|
||||
QByteArray normalizedSlot =
|
||||
QMetaObject::normalizedSignature(signature.toStdString().c_str());
|
||||
|
||||
// does the constructed normalized method exist?
|
||||
methodIndex = object->metaObject()->indexOfSlot(normalizedSlot.toStdString().c_str());
|
||||
|
||||
if (methodIndex >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodIndex < 0) {
|
||||
qCDebug(networking) << "PacketReceiver::registerListener expected a slot with one of the following signatures:"
|
||||
<< possibleSignatures.toList() << "- but such a slot was not found."
|
||||
<< "Could not complete listener registration for type" << type;
|
||||
}
|
||||
|
||||
Q_ASSERT(methodIndex >= 0);
|
||||
|
||||
// return the converted QMetaMethod
|
||||
if (methodIndex >= 0) {
|
||||
return object->metaObject()->method(methodIndex);
|
||||
} else {
|
||||
// if somehow (scripting?) something bad gets in here at runtime that doesn't hit the asserts above
|
||||
// return a non-valid QMetaMethod
|
||||
return QMetaMethod();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object, const QMetaMethod& slot, bool deliverPending) {
|
||||
Q_ASSERT_X(object, "PacketReceiver::registerVerifiedListener", "No object to register");
|
||||
void PacketReceiver::registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending) {
|
||||
Q_ASSERT_X(listener, "PacketReceiver::registerVerifiedListener", "No listener to register");
|
||||
QMutexLocker locker(&_packetListenerLock);
|
||||
|
||||
if (_messageListenerMap.contains(type)) {
|
||||
|
@ -175,7 +109,7 @@ void PacketReceiver::registerVerifiedListener(PacketType type, QObject* object,
|
|||
}
|
||||
|
||||
// add the mapping
|
||||
_messageListenerMap[type] = { QPointer<QObject>(object), slot, deliverPending };
|
||||
_messageListenerMap[type] = { listener, deliverPending };
|
||||
}
|
||||
|
||||
void PacketReceiver::unregisterListener(QObject* listener) {
|
||||
|
@ -188,7 +122,7 @@ void PacketReceiver::unregisterListener(QObject* listener) {
|
|||
auto it = _messageListenerMap.begin();
|
||||
|
||||
while (it != _messageListenerMap.end()) {
|
||||
if (it.value().object == listener) {
|
||||
if (it.value().listener->getObject() == listener) {
|
||||
it = _messageListenerMap.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
@ -261,7 +195,7 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
QMutexLocker packetListenerLocker(&_packetListenerLock);
|
||||
|
||||
auto it = _messageListenerMap.find(receivedMessage->getType());
|
||||
if (it != _messageListenerMap.end() && it->method.isValid()) {
|
||||
if (it != _messageListenerMap.end() && !it->listener.isNull()) {
|
||||
|
||||
auto listener = it.value();
|
||||
|
||||
|
@ -271,36 +205,19 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
|
||||
bool success = false;
|
||||
|
||||
Qt::ConnectionType connectionType;
|
||||
bool isDirectConnect = false;
|
||||
// check if this is a directly connected listener
|
||||
{
|
||||
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
||||
connectionType = _directlyConnectedObjects.contains(listener.object) ? Qt::DirectConnection : Qt::AutoConnection;
|
||||
isDirectConnect = _directlyConnectedObjects.contains(listener.listener->getObject());
|
||||
}
|
||||
|
||||
QMetaMethod metaMethod = listener.method;
|
||||
|
||||
static const QByteArray QSHAREDPOINTER_NODE_NORMALIZED = QMetaObject::normalizedType("QSharedPointer<Node>");
|
||||
static const QByteArray SHARED_NODE_NORMALIZED = QMetaObject::normalizedType("SharedNodePointer");
|
||||
|
||||
// one final check on the QPointer before we go to invoke
|
||||
if (listener.object) {
|
||||
if (metaMethod.parameterTypes().contains(SHARED_NODE_NORMALIZED)) {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage),
|
||||
Q_ARG(SharedNodePointer, matchingNode));
|
||||
|
||||
} else if (metaMethod.parameterTypes().contains(QSHAREDPOINTER_NODE_NORMALIZED)) {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage),
|
||||
Q_ARG(QSharedPointer<Node>, matchingNode));
|
||||
|
||||
if (listener.listener->getObject()) {
|
||||
if (isDirectConnect) {
|
||||
success = listener.listener->invokeDirectly(receivedMessage, matchingNode);
|
||||
} else {
|
||||
success = metaMethod.invoke(listener.object,
|
||||
connectionType,
|
||||
Q_ARG(QSharedPointer<ReceivedMessage>, receivedMessage));
|
||||
success = listener.listener->invokeWithQt(receivedMessage, matchingNode);
|
||||
}
|
||||
} else {
|
||||
qCDebug(networking).nospace() << "Listener for packet " << receivedMessage->getType()
|
||||
|
@ -310,19 +227,19 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer<ReceivedMessage> recei
|
|||
// if it exists, remove the listener from _directlyConnectedObjects
|
||||
{
|
||||
QMutexLocker directConnectLocker(&_directConnectSetMutex);
|
||||
_directlyConnectedObjects.remove(listener.object);
|
||||
_directlyConnectedObjects.remove(listener.listener->getObject());
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
qCDebug(networking).nospace() << "Error delivering packet " << receivedMessage->getType() << " to listener "
|
||||
<< listener.object << "::" << qPrintable(listener.method.methodSignature());
|
||||
<< listener.listener->getObject();
|
||||
}
|
||||
|
||||
} else if (it == _messageListenerMap.end()) {
|
||||
qCWarning(networking) << "No listener found for packet type" << receivedMessage->getType();
|
||||
|
||||
// insert a dummy listener so we don't print this again
|
||||
_messageListenerMap.insert(receivedMessage->getType(), { nullptr, QMetaMethod(), false });
|
||||
_messageListenerMap.insert(receivedMessage->getType(), { ListenerReferencePointer(), false });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QEnableSharedFromThis>
|
||||
|
||||
#include "NLPacket.h"
|
||||
#include "NLPacketList.h"
|
||||
|
@ -29,6 +29,7 @@
|
|||
#include "udt/PacketHeaders.h"
|
||||
|
||||
class EntityEditPacketSender;
|
||||
class Node;
|
||||
class OctreePacketProcessor;
|
||||
|
||||
namespace std {
|
||||
|
@ -42,6 +43,22 @@ namespace std {
|
|||
|
||||
class PacketReceiver : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class ListenerReference : public QEnableSharedFromThis<ListenerReference> {
|
||||
public:
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) = 0;
|
||||
bool invokeWithQt(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const = 0;
|
||||
virtual QObject* getObject() const = 0;
|
||||
};
|
||||
typedef QSharedPointer<ListenerReference> ListenerReferencePointer;
|
||||
|
||||
template<class T>
|
||||
static ListenerReferencePointer makeUnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>));
|
||||
|
||||
template <class T>
|
||||
static ListenerReferencePointer makeSourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>));
|
||||
|
||||
public:
|
||||
using PacketTypeList = std::vector<PacketType>;
|
||||
|
||||
|
@ -55,8 +72,8 @@ public:
|
|||
// If deliverPending is false, ReceivedMessage will only be delivered once all packets for the message have
|
||||
// been received. If deliverPending is true, ReceivedMessage will be delivered as soon as the first packet
|
||||
// for the message is received.
|
||||
bool registerListener(PacketType type, QObject* listener, const char* slot, bool deliverPending = false);
|
||||
bool registerListenerForTypes(PacketTypeList types, QObject* listener, const char* slot);
|
||||
bool registerListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
|
||||
bool registerListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
|
||||
void unregisterListener(QObject* listener);
|
||||
|
||||
void handleVerifiedPacket(std::unique_ptr<udt::Packet> packet);
|
||||
|
@ -64,9 +81,34 @@ public:
|
|||
void handleMessageFailure(HifiSockAddr from, udt::Packet::MessageNumber messageNumber);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
class UnsourcedListenerReference : public ListenerReference {
|
||||
public:
|
||||
inline UnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>));
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const { return false; }
|
||||
virtual QObject* getObject() const { return _target; }
|
||||
|
||||
private:
|
||||
QPointer<T> _target;
|
||||
void (T::*_slot)(QSharedPointer<ReceivedMessage>);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class SourcedListenerReference : public ListenerReference {
|
||||
public:
|
||||
inline SourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>));
|
||||
virtual bool invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode);
|
||||
virtual bool isSourced() const { return true; }
|
||||
virtual QObject* getObject() const { return _target; }
|
||||
|
||||
private:
|
||||
QPointer<T> _target;
|
||||
void (T::*_slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>);
|
||||
};
|
||||
|
||||
struct Listener {
|
||||
QPointer<QObject> object;
|
||||
QMetaMethod method;
|
||||
ListenerReferencePointer listener;
|
||||
bool deliverPending;
|
||||
};
|
||||
|
||||
|
@ -74,11 +116,11 @@ private:
|
|||
|
||||
// these are brutal hacks for now - ideally GenericThread / ReceivedPacketProcessor
|
||||
// should be changed to have a true event loop and be able to handle our QMetaMethod::invoke
|
||||
void registerDirectListenerForTypes(PacketTypeList types, QObject* listener, const char* slot);
|
||||
void registerDirectListener(PacketType type, QObject* listener, const char* slot);
|
||||
void registerDirectListenerForTypes(PacketTypeList types, const ListenerReferencePointer& listener);
|
||||
void registerDirectListener(PacketType type, const ListenerReferencePointer& listener);
|
||||
|
||||
QMetaMethod matchingMethodForListener(PacketType type, QObject* object, const char* slot) const;
|
||||
void registerVerifiedListener(PacketType type, QObject* listener, const QMetaMethod& slot, bool deliverPending = false);
|
||||
bool matchingMethodForListener(PacketType type, const ListenerReferencePointer& listener) const;
|
||||
void registerVerifiedListener(PacketType type, const ListenerReferencePointer& listener, bool deliverPending = false);
|
||||
|
||||
QMutex _packetListenerLock;
|
||||
QHash<PacketType, Listener> _messageListenerMap;
|
||||
|
@ -93,4 +135,42 @@ private:
|
|||
friend class OctreePacketProcessor;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::ListenerReferencePointer PacketReceiver::makeUnsourcedListenerReference(T* target, void (T::* slot)(QSharedPointer<ReceivedMessage>)) {
|
||||
return QSharedPointer<UnsourcedListenerReference<T>>::create(target, slot);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::ListenerReferencePointer PacketReceiver::makeSourcedListenerReference(T* target, void (T::* slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>)) {
|
||||
return QSharedPointer<SourcedListenerReference<T>>::create(target, slot);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::UnsourcedListenerReference<T>::UnsourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>)) :
|
||||
_target(target),_slot(slot) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool PacketReceiver::UnsourcedListenerReference<T>::invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>&) {
|
||||
if (_target.isNull()) {
|
||||
return false;
|
||||
}
|
||||
(_target->*_slot)(receivedMessagePointer);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PacketReceiver::SourcedListenerReference<T>::SourcedListenerReference(T* target, void (T::*slot)(QSharedPointer<ReceivedMessage>, QSharedPointer<Node>)) :
|
||||
_target(target),_slot(slot) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool PacketReceiver::SourcedListenerReference<T>::invokeDirectly(const QSharedPointer<ReceivedMessage>& receivedMessagePointer, const QSharedPointer<Node>& sourceNode) {
|
||||
if (_target.isNull()) {
|
||||
return false;
|
||||
}
|
||||
(_target->*_slot)(receivedMessagePointer, sourceNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // hifi_PacketReceiver_h
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
#include <cmath>
|
||||
#include <assert.h>
|
||||
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
|
|
@ -15,8 +15,14 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <StatTracker.h>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
QString ResourceRequest::toHttpDateString(uint64_t msecsSinceEpoch) {
|
||||
return QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch)
|
||||
.toString("ddd, dd MMM yyyy hh:mm:ss 'GMT'")
|
||||
.toLatin1();
|
||||
}
|
||||
|
||||
void ResourceRequest::send() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
void setCacheEnabled(bool value) { _cacheEnabled = value; }
|
||||
void setByteRange(ByteRange byteRange) { _byteRange = byteRange; }
|
||||
|
||||
static QString toHttpDateString(uint64_t msecsSinceEpoch);
|
||||
public slots:
|
||||
void send();
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ void OctreePersistThread::start() {
|
|||
cleanupOldReplacementBackups();
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileReply, this, "handleOctreeDataFileReply");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataFileReply,
|
||||
PacketReceiver::makeUnsourcedListenerReference<OctreePersistThread>(this, &OctreePersistThread::handleOctreeDataFileReply));
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
|
|
@ -260,6 +260,7 @@ void Font::read(QIODevice& in) {
|
|||
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
|
||||
_texture->setStoredMipFormat(formatMip);
|
||||
_texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
_texture->setImportant(true);
|
||||
}
|
||||
|
||||
void Font::setupGPU() {
|
||||
|
|
|
@ -85,11 +85,25 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
|
|||
|
||||
Lock lock(_containerLock);
|
||||
if (_scriptCache.contains(url) && !forceDownload) {
|
||||
auto scriptContent = _scriptCache[url];
|
||||
lock.unlock();
|
||||
qCDebug(scriptengine) << "Found script in cache:" << url.fileName();
|
||||
contentAvailable(url.toString(), scriptContent, true, true, STATUS_CACHED);
|
||||
} else {
|
||||
auto entry = _scriptCache[url];
|
||||
if (url.isLocalFile() || url.scheme().isEmpty()) {
|
||||
auto modifiedTime = QFileInfo(url.toLocalFile()).lastModified();
|
||||
QString localTime = ResourceRequest::toHttpDateString(modifiedTime.toMSecsSinceEpoch());
|
||||
QString cachedTime = entry["last-modified"].toString();
|
||||
if (cachedTime != localTime) {
|
||||
forceDownload = true;
|
||||
qCDebug(scriptengine) << "Found script in cache, but local file modified; reloading:" << url.fileName()
|
||||
<< "(memory:" << cachedTime << "disk:" << localTime << ")";
|
||||
}
|
||||
}
|
||||
if (!forceDownload) {
|
||||
lock.unlock();
|
||||
qCDebug(scriptengine) << "Found script in cache:" << url.fileName();
|
||||
contentAvailable(url.toString(), entry["data"].toString(), true, true, STATUS_CACHED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto& scriptRequest = _activeScriptRequests[url];
|
||||
bool alreadyWaiting = scriptRequest.scriptUsers.size() > 0;
|
||||
scriptRequest.scriptUsers.push_back(contentAvailable);
|
||||
|
@ -140,7 +154,10 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
|
|||
|
||||
_activeScriptRequests.remove(url);
|
||||
|
||||
_scriptCache[url] = scriptContent = req->getData();
|
||||
_scriptCache[url] = {
|
||||
{ "data", scriptContent = req->getData() },
|
||||
{ "last-modified", req->property("last-modified") },
|
||||
};
|
||||
} else {
|
||||
auto result = req->getResult();
|
||||
bool irrecoverable =
|
||||
|
@ -177,7 +194,7 @@ void ScriptCache::scriptContentAvailable(int maxRetries) {
|
|||
allCallbacks = scriptRequest.scriptUsers;
|
||||
|
||||
if (_scriptCache.contains(url)) {
|
||||
scriptContent = _scriptCache[url];
|
||||
scriptContent = _scriptCache[url]["data"].toString();
|
||||
}
|
||||
_activeScriptRequests.remove(url);
|
||||
qCWarning(scriptengine) << "Error loading script from URL (" << status <<")";
|
||||
|
|
|
@ -60,7 +60,7 @@ private:
|
|||
Mutex _containerLock;
|
||||
QMap<QUrl, ScriptRequest> _activeScriptRequests;
|
||||
|
||||
QHash<QUrl, QString> _scriptCache;
|
||||
QHash<QUrl, QVariantMap> _scriptCache;
|
||||
QMultiMap<QUrl, ScriptUser*> _scriptUsers;
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
|
|||
// This message was sent by one of our script engines, let's try to see if we can find the source.
|
||||
// Note that the first entry in the backtrace should be "print" and is somewhat useless to us
|
||||
AbstractLoggerInterface* loggerInterface = AbstractLoggerInterface::get();
|
||||
if (loggerInterface->showSourceDebugging()) {
|
||||
if (loggerInterface && loggerInterface->showSourceDebugging()) {
|
||||
QScriptContext* userContext = context;
|
||||
while (userContext && QScriptContextInfo(userContext).functionType() == QScriptContextInfo::NativeFunction) {
|
||||
userContext = userContext->parentContext();
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDateTime>
|
||||
|
@ -32,6 +36,40 @@ LogHandler& LogHandler::getInstance() {
|
|||
return staticInstance;
|
||||
}
|
||||
|
||||
LogHandler::LogHandler() {
|
||||
QString logOptions = qgetenv("VIRCADIA_LOG_OPTIONS").toLower();
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// Enable color by default if we're on Unix, and output is a tty (so we're not being piped into something)
|
||||
//
|
||||
// On Windows the situation is more complex, and color is supported or not depending on version and
|
||||
// registry settings, so for now it's off by default and it's up to the user to do what's required.
|
||||
if (isatty(fileno(stdout))) {
|
||||
_useColor = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto optionList = logOptions.split(",");
|
||||
|
||||
for (auto option : optionList) {
|
||||
option = option.trimmed();
|
||||
|
||||
if (option == "color") {
|
||||
_useColor = true;
|
||||
} else if (option == "nocolor") {
|
||||
_useColor = false;
|
||||
} else if (option == "process_id") {
|
||||
_shouldOutputProcessID = true;
|
||||
} else if (option == "thread_id") {
|
||||
_shouldOutputThreadID = true;
|
||||
} else if (option == "milliseconds") {
|
||||
_shouldDisplayMilliseconds = true;
|
||||
} else if (option != "") {
|
||||
fprintf(stdout, "Unrecognized option in VIRCADIA_LOG_OPTIONS: '%s'\n", option.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* stringForLogType(LogMsgType msgType) {
|
||||
switch (msgType) {
|
||||
case LogInfo:
|
||||
|
@ -51,6 +89,29 @@ const char* stringForLogType(LogMsgType msgType) {
|
|||
}
|
||||
}
|
||||
|
||||
const char* colorForLogType(LogMsgType msgType) {
|
||||
switch (msgType) {
|
||||
case LogInfo:
|
||||
return "\u001b[37;1m"; // Bold white
|
||||
case LogDebug:
|
||||
return "";
|
||||
case LogWarning:
|
||||
return "\u001b[35;1m"; // Bright magenta
|
||||
case LogCritical:
|
||||
return "\u001b[31;1m"; // Bright red
|
||||
case LogFatal:
|
||||
return "\u001b[31;1m"; // Bright red
|
||||
case LogSuppressed:
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char* colorReset() {
|
||||
return "\u001b[0m";
|
||||
}
|
||||
|
||||
// the following will produce 11/18 13:55:36
|
||||
const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss";
|
||||
|
||||
|
@ -133,7 +194,15 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
|
|||
|
||||
QString logMessage = QString("%1 %2\n").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
|
||||
|
||||
fprintf(stdout, "%s", qPrintable(logMessage));
|
||||
const char* color = "";
|
||||
const char* resetColor = "";
|
||||
|
||||
if (_useColor) {
|
||||
color = colorForLogType(type);
|
||||
resetColor = colorReset();
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s%s%s", color, qPrintable(logMessage), resetColor);
|
||||
#ifdef Q_OS_WIN
|
||||
// On windows, this will output log lines into the Visual Studio "output" tab
|
||||
OutputDebugStringA(qPrintable(logMessage));
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
void setupRepeatedMessageFlusher();
|
||||
|
||||
private:
|
||||
LogHandler() = default;
|
||||
LogHandler();
|
||||
~LogHandler() = default;
|
||||
|
||||
void flushRepeatedMessages();
|
||||
|
@ -66,6 +66,7 @@ private:
|
|||
bool _shouldOutputProcessID { false };
|
||||
bool _shouldOutputThreadID { false };
|
||||
bool _shouldDisplayMilliseconds { false };
|
||||
bool _useColor { false };
|
||||
|
||||
int _currentMessageID { 0 };
|
||||
struct RepeatedMessageRecord {
|
||||
|
|
|
@ -172,31 +172,31 @@ public:
|
|||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* fileOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* fileSaveDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* existingDirectoryDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
Q_INVOKABLE QString assetOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
Q_INVOKABLE ModalDialogListener* assetOpenDialogAsync(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
// Compatibility with QFileDialog::getOpenFileName
|
||||
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getOpenFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
// Compatibility with QFileDialog::getSaveFileName
|
||||
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getSaveFileNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
// Compatibility with QFileDialog::getExistingDirectory
|
||||
static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getExistingDirectoryAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
|
||||
static QString getOpenAssetName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
static ModalDialogListener* getOpenAssetNameAsync(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = QFileDialog::Options());
|
||||
|
||||
Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant());
|
||||
Q_INVOKABLE ModalDialogListener* inputDialogAsync(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant());
|
||||
|
@ -209,12 +209,12 @@ public:
|
|||
// Compatibility with QInputDialog::getText
|
||||
static QString getText(void* ignored, const QString & title, const QString & label,
|
||||
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
Qt::WindowFlags flags = Qt::WindowFlags(), Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getText(OffscreenUi::ICON_NONE, title, label, text, ok);
|
||||
}
|
||||
// Compatibility with QInputDialog::getItem
|
||||
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = Qt::WindowFlags(),
|
||||
Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getItem(OffscreenUi::ICON_NONE, title, label, items, current, editable, ok);
|
||||
}
|
||||
|
@ -222,12 +222,12 @@ public:
|
|||
// Compatibility with QInputDialog::getText
|
||||
static ModalDialogListener* getTextAsync(void* ignored, const QString & title, const QString & label,
|
||||
QLineEdit::EchoMode mode = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
Qt::WindowFlags flags = Qt::WindowFlags(), Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getTextAsync(OffscreenUi::ICON_NONE, title, label, text);
|
||||
}
|
||||
// Compatibility with QInputDialog::getItem
|
||||
static ModalDialogListener* getItemAsync(void *ignored, const QString & title, const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0,
|
||||
int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = Qt::WindowFlags(),
|
||||
Qt::InputMethodHints inputMethodHints = Qt::ImhNone) {
|
||||
return getItemAsync(OffscreenUi::ICON_NONE, title, label, items, current, editable);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
|
||||
// Copyright 2019 Kasen IO
|
||||
//
|
||||
// Authored by: Humbletim (humbletim@gmail.com)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Supporting file containing all QtScript specific integration.
|
||||
// Supporting file containing all QtScript specific integration.
|
||||
|
||||
#ifndef EXAMPLE_SCRIPT_PLUGIN_H
|
||||
#define EXAMPLE_SCRIPT_PLUGIN_H
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
// Created by Kasen IO on 2019.07.14 | realities.dev | kasenvr@gmail.com
|
||||
// Copyright 2019 Kasen IO
|
||||
//
|
||||
// Authored by: Humbletim (humbletim@gmail.com)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Example of prototyping new JS APIs by leveraging the existing plugin system.
|
||||
// Example of prototyping new JS APIs by leveraging the existing plugin system.
|
||||
|
||||
#include "ExampleScriptPlugin.h"
|
||||
|
||||
|
|
|
@ -1696,7 +1696,7 @@ function recursiveDelete(entities, childrenList, deletedIDs, entityHostType) {
|
|||
}
|
||||
|
||||
function unparentSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var selectedEntities = selectionManager.selections;
|
||||
var parentCheck = false;
|
||||
|
||||
|
@ -1720,6 +1720,9 @@ function unparentSelectedEntities() {
|
|||
} else {
|
||||
Window.notify("Entity unparented");
|
||||
}
|
||||
//Refresh
|
||||
entityListTool.sendUpdate();
|
||||
selectionManager._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
if (selectedEntities.length > 1) {
|
||||
|
@ -1730,11 +1733,11 @@ function unparentSelectedEntities() {
|
|||
}
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected to unparent");
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
function parentSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var selectedEntities = selectionManager.selections;
|
||||
if (selectedEntities.length <= 1) {
|
||||
audioFeedback.rejection();
|
||||
|
@ -1756,17 +1759,20 @@ function parentSelectedEntities() {
|
|||
if (parentCheck) {
|
||||
audioFeedback.confirmation();
|
||||
Window.notify("Entities parented");
|
||||
//Refresh
|
||||
entityListTool.sendUpdate();
|
||||
selectionManager._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notify("Entities are already parented to last");
|
||||
}
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected to parent");
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
function deleteSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (SelectionManager.hasSelection() && SelectionManager.hasUnlockedSelection()) {
|
||||
var deletedIDs = [];
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
|
@ -1797,6 +1803,9 @@ function deleteSelectedEntities() {
|
|||
pushCommandForSelections([], savedProperties);
|
||||
entityListTool.deleteEntities(deletedIDs);
|
||||
}
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2963,4 +2972,22 @@ function zoneSortOrder(a, b) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
function getParentState(id) {
|
||||
var state = "NONE";
|
||||
var properties = Entities.getEntityProperties(id, ["parentID"]);
|
||||
var children = Entities.getChildrenIDs(id);
|
||||
if (properties.parentID !== Uuid.NULL) {
|
||||
if (children.length > 0) {
|
||||
state = "PARENT_CHILDREN";
|
||||
} else {
|
||||
state = "CHILDREN";
|
||||
}
|
||||
} else {
|
||||
if (children.length > 0) {
|
||||
state = "PARENT";
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -190,7 +190,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
PROFILE("getMultipleProperties", function () {
|
||||
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked',
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
|
||||
'skybox.url', 'ambientLight.url']);
|
||||
'skybox.url', 'ambientLight.url', 'created', 'lastEdited']);
|
||||
for (var i = 0; i < multipleProperties.length; i++) {
|
||||
var properties = multipleProperties[i];
|
||||
|
||||
|
@ -203,6 +203,17 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
} else if (properties.type === "Image") {
|
||||
url = properties.imageURL;
|
||||
}
|
||||
|
||||
var parentStatus = getParentState(ids[i]);
|
||||
var parentState = "";
|
||||
if (parentStatus === "PARENT") {
|
||||
parentState = "A";
|
||||
} else if (parentStatus === "CHILDREN") {
|
||||
parentState = "C";
|
||||
} else if (parentStatus === "PARENT_CHILDREN") {
|
||||
parentState = "B";
|
||||
}
|
||||
|
||||
entities.push({
|
||||
id: ids[i],
|
||||
name: properties.name,
|
||||
|
@ -222,7 +233,10 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
isBaked: entityIsBaked(properties),
|
||||
drawCalls: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.drawCalls) : ""),
|
||||
hasScript: properties.script !== ""
|
||||
hasScript: properties.script !== "",
|
||||
parentState: parentState,
|
||||
created: formatToStringDateTime(properties.created),
|
||||
lastEdited: formatToStringDateTime(properties.lastEdited)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +256,22 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
});
|
||||
};
|
||||
|
||||
function formatToStringDateTime(timestamp) {
|
||||
var d = new Date(Math.floor(timestamp/1000));
|
||||
var dateTime = d.getUTCFullYear() + "-" + zeroPad((d.getUTCMonth() + 1), 2) + "-" + zeroPad(d.getUTCDate(), 2);
|
||||
dateTime = dateTime + " " + zeroPad(d.getUTCHours(), 2) + ":" + zeroPad(d.getUTCMinutes(), 2) + ":" + zeroPad(d.getUTCSeconds(), 2);
|
||||
dateTime = dateTime + "." + zeroPad(d.getUTCMilliseconds(), 3);
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
function zeroPad(num, size) {
|
||||
num = num.toString();
|
||||
while (num.length < size) {
|
||||
num = "0" + num;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
function onFileSaveChanged(filename) {
|
||||
Window.saveFileChanged.disconnect(onFileSaveChanged);
|
||||
if (filename !== "") {
|
||||
|
@ -323,6 +353,24 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
unparentSelectedEntities();
|
||||
} else if (data.type === 'hmdMultiSelectMode') {
|
||||
hmdMultiSelectMode = data.value;
|
||||
} else if (data.type === 'selectAllInBox') {
|
||||
selectAllEntitiesInCurrentSelectionBox(false);
|
||||
} else if (data.type === 'selectAllTouchingBox') {
|
||||
selectAllEntitiesInCurrentSelectionBox(true);
|
||||
} else if (data.type === 'selectParent') {
|
||||
SelectionManager.selectParent();
|
||||
} else if (data.type === 'selectTopParent') {
|
||||
SelectionManager.selectTopParent();
|
||||
} else if (data.type === 'addChildrenToSelection') {
|
||||
SelectionManager.addChildrenToSelection();
|
||||
} else if (data.type === 'selectFamily') {
|
||||
SelectionManager.selectFamily();
|
||||
} else if (data.type === 'selectTopFamily') {
|
||||
SelectionManager.selectTopFamily();
|
||||
} else if (data.type === 'teleportToEntity') {
|
||||
SelectionManager.teleportToEntity();
|
||||
} else if (data.type === 'moveEntitySelectionToAvatar') {
|
||||
SelectionManager.moveEntitiesSelectionToAvatar();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -32,19 +32,8 @@
|
|||
</div>
|
||||
<button id="toggle-space-mode" class="hifi-edit-button space-mode-local">Local</button>
|
||||
<input type="button" class="vglyph" id="hmdmultiselect" value="I" style="display: none;" />
|
||||
<input type="button" class="blue vglyph" id="hmdcut" value="D" style="display: none;" />
|
||||
<input type="button" class="blue vglyph" id="hmdcopy" value="B" style="display: none;" />
|
||||
<input type="button" class="blue vglyph" id="hmdpaste" value="E" style="display: none;" />
|
||||
<input type="button" class="green vglyph" id="hmdduplicate" value="A" style="display: none;" />
|
||||
<div>
|
||||
<input type="button" class="vglyph" id="parent" value="K" />
|
||||
<input type="button" class="vglyph" id="unparent" value="L" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="button" class="orange vglyph" id="undo" value="F" />
|
||||
<input type="button" class="orange vglyph" id="redo" value="G" />
|
||||
</div>
|
||||
<input type="button" class="red glyph" id="delete" value="{" />
|
||||
<input type="button" class="normal" id="selection" value="Selection..." />
|
||||
<input type="button" class="normal" id="actions" value="Actions..." />
|
||||
</div>
|
||||
<div id="entity-list">
|
||||
<div id="filter-area">
|
||||
|
@ -102,5 +91,142 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entity-list-menu" id="actions-menu" >
|
||||
<button class="menu-button" id="undo" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Undo</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Z</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="redo" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Redo</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Y</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="hmdcut" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Cut</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-X</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdcopy" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Copy</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-C</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdpaste" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Paste</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-V</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="hmdduplicate" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Duplicate</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-D</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="delete" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Delete</div>
|
||||
<div class = "menu-item-shortcut">Del</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="parent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Parent Entities to the Last Selected</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-P</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="unparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Unparent Entity</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-Shift-P</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="moveEntitySelectionToAvatar" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Move Selected Entities to Avatar</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="entity-list-menu" id="selection-menu" >
|
||||
<button class="menu-button" id="selectall" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-A</div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectnone" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select None</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectinverse" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Inverse Selection</div>
|
||||
<div class = "menu-item-shortcut">Ctrl-I</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="selectallinbox" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All Entities In Box</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectalltouchingbox" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select All Entities Touching Box</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="selectparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Parent</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selecttopparent" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Top Parent</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="addchildrentoselection" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Add Children To Selection</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selectfamily" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Family</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<button class="menu-button" id="selecttopfamily" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Select Top Family</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-button" id="teleport-to-entity" >
|
||||
<div class = "menu-item">
|
||||
<div class = "menu-item-caption">Teleport To Selected Entities</div>
|
||||
<div class = "menu-item-shortcut"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div id="menuBackgroundOverlay" ></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -47,6 +47,14 @@ const COLUMNS = {
|
|||
alwaysShown: true,
|
||||
defaultSortOrder: ASCENDING_SORT,
|
||||
},
|
||||
parentState: {
|
||||
columnHeader: "A",
|
||||
vglyph: true,
|
||||
dropdownLabel: "Hierarchy",
|
||||
propertyID: "parentState",
|
||||
initialWidth: 0.08,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
name: {
|
||||
columnHeader: "Name",
|
||||
propertyID: "name",
|
||||
|
@ -134,6 +142,20 @@ const COLUMNS = {
|
|||
initialWidth: 0.06,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
created: {
|
||||
columnHeader: "Created (UTC)",
|
||||
dropdownLabel: "Creation Date",
|
||||
propertyID: "created",
|
||||
initialWidth: 0.38,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
lastEdited: {
|
||||
columnHeader: "Modified (UTC)",
|
||||
dropdownLabel: "Modification Date",
|
||||
propertyID: "lastEdited",
|
||||
initialWidth: 0.38,
|
||||
defaultSortOrder: DESCENDING_SORT,
|
||||
},
|
||||
};
|
||||
|
||||
const FILTER_TYPES = [
|
||||
|
@ -200,7 +222,10 @@ let elEntityTable,
|
|||
elRefresh,
|
||||
elToggleLocked,
|
||||
elToggleVisible,
|
||||
elHmdMultiSelect,
|
||||
elActionsMenu,
|
||||
elSelectionMenu,
|
||||
elMenuBackgroundOverlay,
|
||||
elHmdMultiSelect,
|
||||
elHmdCopy,
|
||||
elHmdCut,
|
||||
elHmdPaste,
|
||||
|
@ -210,6 +235,18 @@ let elEntityTable,
|
|||
elParent,
|
||||
elUnparent,
|
||||
elDelete,
|
||||
elMoveEntitySelectionToAvatar,
|
||||
elSelectAll,
|
||||
elSelectInverse,
|
||||
elSelectNone,
|
||||
elSelectAllInBox,
|
||||
elSelectAllTouchingBox,
|
||||
elSelectParent,
|
||||
elSelectTopParent,
|
||||
elAddChildrenToSelection,
|
||||
elSelectFamily,
|
||||
elSelectTopFamily,
|
||||
elTeleportToEntity,
|
||||
elFilterTypeMultiselectBox,
|
||||
elFilterTypeText,
|
||||
elFilterTypeOptions,
|
||||
|
@ -252,8 +289,11 @@ function loaded() {
|
|||
elEntityTableScroll = document.getElementById("entity-table-scroll");
|
||||
elRefresh = document.getElementById("refresh");
|
||||
elToggleLocked = document.getElementById("locked");
|
||||
elToggleVisible = document.getElementById("visible");
|
||||
elToggleVisible = document.getElementById("visible");
|
||||
elHmdMultiSelect = document.getElementById("hmdmultiselect");
|
||||
elActionsMenu = document.getElementById("actions");
|
||||
elSelectionMenu = document.getElementById("selection");
|
||||
elMenuBackgroundOverlay = document.getElementById("menuBackgroundOverlay");
|
||||
elHmdCopy = document.getElementById("hmdcopy");
|
||||
elHmdCut = document.getElementById("hmdcut");
|
||||
elHmdPaste = document.getElementById("hmdpaste");
|
||||
|
@ -263,6 +303,18 @@ function loaded() {
|
|||
elParent = document.getElementById("parent");
|
||||
elUnparent = document.getElementById("unparent");
|
||||
elDelete = document.getElementById("delete");
|
||||
elMoveEntitySelectionToAvatar = document.getElementById("moveEntitySelectionToAvatar");
|
||||
elSelectAll = document.getElementById("selectall");
|
||||
elSelectInverse = document.getElementById("selectinverse");
|
||||
elSelectNone = document.getElementById("selectnone");
|
||||
elSelectAllInBox = document.getElementById("selectallinbox");
|
||||
elSelectAllTouchingBox = document.getElementById("selectalltouchingbox");
|
||||
elSelectParent = document.getElementById("selectparent");
|
||||
elSelectTopParent = document.getElementById("selecttopparent");
|
||||
elAddChildrenToSelection = document.getElementById("addchildrentoselection");
|
||||
elSelectFamily = document.getElementById("selectfamily");
|
||||
elSelectTopFamily = document.getElementById("selecttopfamily");
|
||||
elTeleportToEntity = document.getElementById("teleport-to-entity");
|
||||
elFilterTypeMultiselectBox = document.getElementById("filter-type-multiselect-box");
|
||||
elFilterTypeText = document.getElementById("filter-type-text");
|
||||
elFilterTypeOptions = document.getElementById("filter-type-options");
|
||||
|
@ -300,32 +352,146 @@ function loaded() {
|
|||
}
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'hmdMultiSelectMode', value: hmdMultiSelectMode }));
|
||||
};
|
||||
elActionsMenu.onclick = function() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "block";
|
||||
document.getElementById("actions-menu").style.display = "block";
|
||||
};
|
||||
elSelectionMenu.onclick = function() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "block";
|
||||
document.getElementById("selection-menu").style.display = "block";
|
||||
};
|
||||
elMenuBackgroundOverlay.onclick = function() {
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdCopy.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdCut.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'cut' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdPaste.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'paste' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elHmdDuplicate.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'duplicate' }));
|
||||
};
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'parent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elUnparent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'unparent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elUndo.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'undo' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elRedo.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'redo' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elDelete.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elMoveEntitySelectionToAvatar.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'moveEntitySelectionToAvatar' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAll.onclick = function() {
|
||||
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
selection = visibleEntityIDs;
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection, false);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectInverse.onclick = function() {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
visibleEntityIDs.forEach(function(id) {
|
||||
if (!selectedEntities.includes(id)) {
|
||||
selection.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection, false);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectNone.onclick = function() {
|
||||
updateSelectedEntities([], false);
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": []
|
||||
}));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAllInBox.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectAllInBox' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectAllTouchingBox.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectAllTouchingBox' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectParent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectTopParent.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectTopParent' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elAddChildrenToSelection.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'addChildrenToSelection' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectFamily.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectFamily' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elSelectTopFamily.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'selectTopFamily' }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elTeleportToEntity.onclick = function () {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: "teleportToEntity" }));
|
||||
closeAllEntityListMenu();
|
||||
};
|
||||
elToggleSpaceMode.onclick = function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleSpaceMode' }));
|
||||
|
@ -384,9 +550,12 @@ function loaded() {
|
|||
elTh.setAttribute("id", thID);
|
||||
elTh.setAttribute("columnIndex", columnIndex);
|
||||
elTh.setAttribute("columnID", columnID);
|
||||
if (columnData.glyph) {
|
||||
if (columnData.glyph || columnData.vglyph) {
|
||||
let elGlyph = document.createElement("span");
|
||||
elGlyph.className = "glyph";
|
||||
if (columnData.vglyph) {
|
||||
elGlyph.className = "vglyph";
|
||||
}
|
||||
elGlyph.innerHTML = columnData.columnHeader;
|
||||
elTh.appendChild(elGlyph);
|
||||
} else {
|
||||
|
@ -686,10 +855,13 @@ function loaded() {
|
|||
isBaked: entity.isBaked,
|
||||
drawCalls: displayIfNonZero(entity.drawCalls),
|
||||
hasScript: entity.hasScript,
|
||||
parentState: entity.parentState,
|
||||
created: entity.created,
|
||||
lastEdited: entity.lastEdited,
|
||||
elRow: null, // if this entity has a visible row element assigned to it
|
||||
selected: false // if this entity is selected for edit regardless of having a visible row
|
||||
};
|
||||
|
||||
|
||||
entities.push(entityData);
|
||||
entitiesByID[entityData.id] = entityData;
|
||||
});
|
||||
|
@ -871,7 +1043,7 @@ function loaded() {
|
|||
|
||||
function updateSelectedEntities(selectedIDs, autoScroll) {
|
||||
let notFound = false;
|
||||
|
||||
|
||||
// reset all currently selected entities and their rows first
|
||||
selectedEntities.forEach(function(id) {
|
||||
let entity = entitiesByID[id];
|
||||
|
@ -882,7 +1054,7 @@ function loaded() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// then reset selected entities list with newly selected entities and set them selected
|
||||
selectedEntities = [];
|
||||
selectedIDs.forEach(function(id) {
|
||||
|
@ -945,6 +1117,8 @@ function loaded() {
|
|||
let elCell = elRow.childNodes[i];
|
||||
if (column.data.glyph) {
|
||||
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
|
||||
} else if (column.data.vglyph) {
|
||||
elCell.innerHTML = itemData[column.data.propertyID];
|
||||
} else {
|
||||
let value = itemData[column.data.propertyID];
|
||||
if (column.data.format) {
|
||||
|
@ -1032,6 +1206,9 @@ function loaded() {
|
|||
let column = columnsByID[columnID];
|
||||
let visible = column.elTh.style.visibility !== "hidden";
|
||||
let className = column.data.glyph ? "glyph" : "";
|
||||
if (column.data.vglyph) {
|
||||
className = "vglyph";
|
||||
}
|
||||
className += visible ? "" : " hidden";
|
||||
return className;
|
||||
}
|
||||
|
@ -1381,7 +1558,7 @@ function loaded() {
|
|||
break;
|
||||
}
|
||||
|
||||
if (controlKey && keyCodeString === "A") {
|
||||
if (controlKey && !shiftKey && !altKey && keyCodeString === "A") {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
|
@ -1404,6 +1581,32 @@ function loaded() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (controlKey && !shiftKey && !altKey && keyCodeString === "I") {
|
||||
let visibleEntityIDs = visibleEntities.map(visibleEntity => visibleEntity.id);
|
||||
let selectionIncludesAllVisibleEntityIDs = visibleEntityIDs.every(visibleEntityID => {
|
||||
return selectedEntities.includes(visibleEntityID);
|
||||
});
|
||||
|
||||
let selection = [];
|
||||
|
||||
if (!selectionIncludesAllVisibleEntityIDs) {
|
||||
visibleEntityIDs.forEach(function(id) {
|
||||
if (!selectedEntities.includes(id)) {
|
||||
selection.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedEntities(selection);
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
"type": "selectionUpdate",
|
||||
"focus": false,
|
||||
"entityIds": selection
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: 'keyUpEvent',
|
||||
|
@ -1454,16 +1657,8 @@ function loaded() {
|
|||
} else if (data.type === "confirmHMDstate") {
|
||||
if (data.isHmd) {
|
||||
document.getElementById("hmdmultiselect").style.display = "inline";
|
||||
document.getElementById("hmdcopy").style.display = "inline";
|
||||
document.getElementById("hmdcut").style.display = "inline";
|
||||
document.getElementById("hmdpaste").style.display = "inline";
|
||||
document.getElementById("hmdduplicate").style.display = "inline";
|
||||
} else {
|
||||
document.getElementById("hmdmultiselect").style.display = "none";
|
||||
document.getElementById("hmdcopy").style.display = "none";
|
||||
document.getElementById("hmdcut").style.display = "none";
|
||||
document.getElementById("hmdpaste").style.display = "none";
|
||||
document.getElementById("hmdduplicate").style.display = "none";
|
||||
document.getElementById("hmdmultiselect").style.display = "none";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1489,4 +1684,11 @@ function loaded() {
|
|||
$(window).blur(function() {
|
||||
entityListContextMenu.close();
|
||||
});
|
||||
|
||||
function closeAllEntityListMenu() {
|
||||
document.getElementById("menuBackgroundOverlay").style.display = "none";
|
||||
document.getElementById("selection-menu").style.display = "none";
|
||||
document.getElementById("actions-menu").style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1618,7 +1618,8 @@ const GROUPS = [
|
|||
type: "vec3",
|
||||
vec3Type: "pyr",
|
||||
multiplier: DEGREES_TO_RADIANS,
|
||||
decimals: 4,
|
||||
decimals: 6,
|
||||
step: 1,
|
||||
subLabels: [ "x", "y", "z" ],
|
||||
unit: "deg/s",
|
||||
propertyID: "localAngularVelocity",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
const SPACE_LOCAL = "local";
|
||||
const SPACE_WORLD = "world";
|
||||
const HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
|
||||
const MIN_DISTANCE_TO_REZ_FROM_AVATAR = 3;
|
||||
|
||||
Script.include([
|
||||
"../../libraries/controllers.js",
|
||||
|
@ -26,7 +27,6 @@ Script.include([
|
|||
"../../libraries/utils.js"
|
||||
]);
|
||||
|
||||
|
||||
function deepCopy(v) {
|
||||
return JSON.parse(JSON.stringify(v));
|
||||
}
|
||||
|
@ -636,6 +636,141 @@ SelectionManager = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
that.teleportToEntity = function() {
|
||||
if (that.hasSelection()) {
|
||||
var distanceFromTarget = MIN_DISTANCE_TO_REZ_FROM_AVATAR + Math.max(Math.max(that.worldDimensions.x, that.worldDimensions.y), that.worldDimensions.z);
|
||||
var teleportTargetPosition = Vec3.sum(that.worldPosition, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: distanceFromTarget }));
|
||||
MyAvatar.goToLocation(teleportTargetPosition, false);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.moveEntitiesSelectionToAvatar = function() {
|
||||
if (that.hasSelection() && that.hasUnlockedSelection()) {
|
||||
that.saveProperties();
|
||||
var distanceFromTarget = MIN_DISTANCE_TO_REZ_FROM_AVATAR + Math.max(Math.max(that.worldDimensions.x, that.worldDimensions.y), that.worldDimensions.z);
|
||||
var targetPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -distanceFromTarget }));
|
||||
// editing a parent will cause all the children to automatically follow along, so don't
|
||||
// edit any entity who has an ancestor in that.selections
|
||||
var toMove = that.selections.filter(function (selection) {
|
||||
if (that.selections.indexOf(that.savedProperties[selection].parentID) >= 0) {
|
||||
return false; // a parent is also being moved, so don't issue an edit for this entity
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
for (var i = 0; i < toMove.length; i++) {
|
||||
var id = toMove[i];
|
||||
var properties = that.savedProperties[id];
|
||||
var relativePosition = Vec3.subtract(properties.position, that.worldPosition);
|
||||
var newPosition = Vec3.sum(relativePosition, targetPosition);
|
||||
Entities.editEntity(id, { "position": newPosition });
|
||||
}
|
||||
that._update(false, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected or the selection has locked entities.");
|
||||
}
|
||||
};
|
||||
|
||||
that.selectParent = function() {
|
||||
if (that.hasSelection()) {
|
||||
var currentSelection = that.selections;
|
||||
that.selections = [];
|
||||
for (var i = 0; i < currentSelection.length; i++) {
|
||||
var properties = Entities.getEntityProperties(currentSelection[i], ['parentID']);
|
||||
if (properties.parentID !== Uuid.NULL) {
|
||||
that.selections.push(properties.parentID);
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.selectTopParent = function() {
|
||||
if (that.hasSelection()) {
|
||||
var currentSelection = that.selections;
|
||||
that.selections = [];
|
||||
for (var i = 0; i < currentSelection.length; i++) {
|
||||
var topParentId = getTopParent(currentSelection[i]);
|
||||
if (topParentId !== Uuid.NULL) {
|
||||
that.selections.push(topParentId);
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
function getTopParent(id) {
|
||||
var topParentId = Uuid.NULL;
|
||||
var properties = Entities.getEntityProperties(id, ['parentID']);
|
||||
if (properties.parentID === Uuid.NULL) {
|
||||
topParentId = id;
|
||||
} else {
|
||||
topParentId = getTopParent(properties.parentID);
|
||||
}
|
||||
return topParentId;
|
||||
}
|
||||
|
||||
that.addChildrenToSelection = function() {
|
||||
if (that.hasSelection()) {
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
var childrenIDs = Entities.getChildrenIDs(that.selections[i]);
|
||||
var collectNewChildren;
|
||||
var j;
|
||||
var k = 0;
|
||||
do {
|
||||
collectNewChildren = Entities.getChildrenIDs(childrenIDs[k]);
|
||||
if (collectNewChildren.length > 0) {
|
||||
for (j = 0; j < collectNewChildren.length; j++) {
|
||||
childrenIDs.push(collectNewChildren[j]);
|
||||
}
|
||||
}
|
||||
k++;
|
||||
} while (k < childrenIDs.length);
|
||||
if (childrenIDs.length > 0) {
|
||||
for (j = 0; j < childrenIDs.length; j++) {
|
||||
that.selections.push(childrenIDs[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
that._update(true, this);
|
||||
} else {
|
||||
audioFeedback.rejection();
|
||||
Window.notifyEditError("You have nothing selected.");
|
||||
}
|
||||
};
|
||||
|
||||
that.hasUnlockedSelection = function() {
|
||||
var selectionNotLocked = true;
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
var properties = Entities.getEntityProperties(that.selections[i], ['locked']);
|
||||
if (properties.locked) {
|
||||
selectionNotLocked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selectionNotLocked;
|
||||
};
|
||||
|
||||
that.selectFamily = function() {
|
||||
that.selectParent();
|
||||
that.addChildrenToSelection();
|
||||
};
|
||||
|
||||
that.selectTopFamily = function() {
|
||||
that.selectTopParent();
|
||||
that.addChildrenToSelection();
|
||||
};
|
||||
|
||||
return that;
|
||||
})();
|
||||
|
||||
|
@ -660,8 +795,10 @@ SelectionDisplay = (function() {
|
|||
const COLOR_HOVER = { red: 255, green: 220, blue: 82 };
|
||||
const COLOR_DUPLICATOR = { red: 162, green: 0, blue: 255 };
|
||||
const COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 };
|
||||
const COLOR_BOUNDING_EDGE = { red: 128, green: 128, blue: 128 };
|
||||
const COLOR_SCALE_CUBE = { red: 160, green: 160, blue: 160 };
|
||||
const COLOR_BOUNDING_EDGE = { red: 160, green: 160, blue: 160 };
|
||||
const COLOR_BOUNDING_EDGE_PARENT = { red: 194, green: 123, blue: 0 };
|
||||
const COLOR_BOUNDING_EDGE_CHILDREN = { red: 0, green: 168, blue: 214 };
|
||||
const COLOR_SCALE_CUBE = { red: 192, green: 192, blue: 192 };
|
||||
const COLOR_DEBUG_PICK_PLANE = { red: 255, green: 255, blue: 255 };
|
||||
const COLOR_DEBUG_PICK_PLANE_HIT = { red: 255, green: 165, blue: 0 };
|
||||
|
||||
|
@ -1791,6 +1928,18 @@ SelectionDisplay = (function() {
|
|||
var rotationZ = Quat.multiply(rotation, localRotationZ);
|
||||
worldRotationZ = rotationZ;
|
||||
|
||||
var handleBoundingBoxColor = COLOR_BOUNDING_EDGE;
|
||||
if (SelectionManager.selections.length === 1) {
|
||||
var parentState = getParentState(SelectionManager.selections[0]);
|
||||
if (parentState === "CHILDREN") {
|
||||
handleBoundingBoxColor = COLOR_BOUNDING_EDGE_CHILDREN;
|
||||
} else {
|
||||
if (parentState === "PARENT" || parentState === "PARENT_CHILDREN") {
|
||||
handleBoundingBoxColor = COLOR_BOUNDING_EDGE_PARENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var selectionBoxGeometry = {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
|
@ -1912,6 +2061,7 @@ SelectionDisplay = (function() {
|
|||
Entities.editEntity(handleBoundingBox, {
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
color: handleBoundingBoxColor,
|
||||
dimensions: dimensions
|
||||
});
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ Rectangle {
|
|||
|
||||
Text {
|
||||
id: text1
|
||||
text: qsTr("Material URL (Optional)")
|
||||
text: qsTr("Material URL <i>(Optional)</i>")
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ Rectangle {
|
|||
|
||||
Text {
|
||||
id: text1
|
||||
text: qsTr("Model URL")
|
||||
text: qsTr("Model URL <i>(.fbx, .fst, .glb, .gltf, .obj, .gz)</i>")
|
||||
color: "#ffffff"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
|
|
@ -415,6 +415,13 @@ input[type=button].glyph, button.hifi-edit-button.glyph {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].normal, button.hifi-edit-button.normal {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
text-transform: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].vglyph, button.hifi-edit-button.vglyph {
|
||||
font-family: Vircadia-Glyphs;
|
||||
font-size: 20px;
|
||||
|
@ -1236,7 +1243,6 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
|||
background: #2e2e2e url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACKSURBVChTjdAxDsMgDAXQT4UYuQIzCwsSKxsSJ4YDoByDY7AwUOG2aZMQqX+xhd9gzIwxA3/k8a7LCCFgraX+Fk4UY4RSCoyxNfwgzjlyzhhjXOEvSimhtUbvB3hGUkp472m2wxUKIaD3TnOCd6jWim3bvlBrfdjJOUeolEJoZj/4PMH83bl/BXgCWSs2Z09IjgoAAAAASUVORK5CYII=) no-repeat bottom right;
|
||||
}
|
||||
|
||||
|
||||
div#grid-section, body#entity-list-body {
|
||||
padding-bottom: 0;
|
||||
margin: 16px;
|
||||
|
@ -1267,12 +1273,6 @@ div#grid-section, body#entity-list-body {
|
|||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
#delete {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
#entity-list {
|
||||
position: relative; /* New positioning context. */
|
||||
}
|
||||
|
@ -1448,6 +1448,11 @@ input[type=button]#export {
|
|||
font-size: 15px;
|
||||
}
|
||||
|
||||
#entity-table-scroll .vglyph {
|
||||
font-family: Vircadia-Glyphs;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#entity-table {
|
||||
margin-top: -28px;
|
||||
margin-bottom: -18px;
|
||||
|
@ -1460,7 +1465,7 @@ input[type=button]#export {
|
|||
background: none;
|
||||
}
|
||||
|
||||
#entity-table .glyph {
|
||||
#entity-table .glyph .vglyph {
|
||||
margin: 0 -2px 0 -2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -1493,11 +1498,11 @@ input[type=button]#export {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
#entity-table th .glyph {
|
||||
#entity-table th .glyph .vglyph {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
}
|
||||
#entity-table th .glyph + .sort-order {
|
||||
#entity-table th .glyph .vglyph + .sort-order {
|
||||
position: relative;
|
||||
left: 4px;
|
||||
}
|
||||
|
@ -1524,7 +1529,7 @@ input[type=button]#export {
|
|||
#entity-table td {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#entity-table td.glyph {
|
||||
#entity-table td .glyph .vglyph {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -1860,3 +1865,71 @@ div.multiZoneSelToolbar {
|
|||
padding: 0px;
|
||||
}
|
||||
|
||||
#menuBackgroundOverlay{
|
||||
background-color:transparent;
|
||||
position:fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
display:none;
|
||||
}
|
||||
|
||||
div.entity-list-menu {
|
||||
position: fixed;
|
||||
display: none;
|
||||
width: 70%;
|
||||
height: 30px;
|
||||
top: 42px;
|
||||
left: 150px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-style: solid;
|
||||
border-color: #505050;
|
||||
border-width: 1px;
|
||||
background-color: #c0c0c0;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.menu-separator{
|
||||
width: 90%;
|
||||
height: 2px;
|
||||
background-color: #505050;
|
||||
}
|
||||
|
||||
button.menu-button {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 0;
|
||||
padding: 6px;
|
||||
text-align: left;
|
||||
background-color: #c0c0c0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button.menu-button:hover {
|
||||
background-color: #00B4EF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
button.menu-button:active {
|
||||
background-color: #00B4EF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.menu-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.menu-item-caption {
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.menu-item-shortcut {
|
||||
float: right;
|
||||
}
|
||||
|
|
|
@ -6,19 +6,18 @@ import boto3
|
|||
import glob
|
||||
from github import Github
|
||||
|
||||
|
||||
def main():
|
||||
bucket_name = os.environ['BUCKET_NAME']
|
||||
bucket_name = os.environ['UPLOAD_BUCKET']
|
||||
upload_prefix = os.environ['UPLOAD_PREFIX']
|
||||
release_number = os.environ['RELEASE_NUMBER']
|
||||
full_prefix = upload_prefix + '/' + release_number[0:-2] + '/' + release_number
|
||||
full_prefix = upload_prefix + '/' + release_number
|
||||
S3 = boto3.client('s3')
|
||||
path = os.path.join(os.getcwd(), os.environ['ARTIFACT_PATTERN'])
|
||||
files = glob.glob(path, recursive=False)
|
||||
for archiveFile in files:
|
||||
filePath, fileName = os.path.split(archiveFile)
|
||||
S3.upload_file(os.path.join(filePath, fileName), bucket_name, full_prefix + '/' + fileName)
|
||||
print("Uploaded Artifact to S3: https://{}.s3-us-west-2.amazonaws.com/{}/{}".format(bucket_name, full_prefix, fileName))
|
||||
print("Uploaded Artifact to S3: https://{}.s3-eu-west-3.amazonaws.com/{}/{}".format(bucket_name, full_prefix, fileName))
|
||||
print("Finished")
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in a new issue