Merge remote-tracking branch 'upstream/master' into scriptEngine

This commit is contained in:
HifiExperiments 2020-11-09 22:00:11 -08:00
commit b3e9c1e8f8
505 changed files with 332553 additions and 13191 deletions

View file

@ -5,6 +5,7 @@ module.exports = {
"ecmaVersion": 5
},
"globals": {
"About": false,
"Account": false,
"Agent": false,
"AnimationCache": false,

View file

@ -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 }}

View file

@ -1,6 +1,6 @@
# General Build Information
*Last Updated on June 27, 2020*
*Last Updated on August 26, 2020*
### OS Specific Build Guides
@ -80,12 +80,19 @@ Where /path/to/directory is the path to a directory where you wish the build fil
// The type of release.
RELEASE_TYPE=PRODUCTION|PR|DEV
// TODO: What do these do?
// The Interface will have a custom default home and startup location.
INITIAL_STARTUP_LOCATION=Location/IP/URL
// Code-signing environment variables must be set during runtime of CMake AND globally when the signing takes place.
HF_PFX_FILE=Path to certificate
HF_PFX_PASSPHRASE=Passphrase for certificate
// Determine the build type
PRODUCTION_BUILD=0|1
PR_BUILD=0|1
STABLE_BUILD=0|1
// TODO: What do these do?
// Determine if to utilize testing or stable Metaverse URLs
USE_STABLE_GLOBAL_SERVICES=1
BUILD_GLOBAL_SERVICES=STABLE
@ -141,6 +148,8 @@ The following build options can be used when running CMake
* BUILD_SERVER
* BUILD_TESTS
* BUILD_TOOLS
* CLIENT_ONLY // Will package only the Interface
* SERVER_ONLY // Will package only the Server
#### Developer Build Options

View file

@ -1,6 +1,6 @@
# Build OSX
*Last Updated on July 13, 2020*
*Last Updated on August 26, 2020*
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only macOS specific instructions are found in this document.
@ -17,7 +17,9 @@ Execute the `Update Shell Profile.command` script that is provided with the inst
### OSX SDK
You will need the OSX SDK for building. The easiest way to get this is to install Xcode from the App Store.
You will need version `10.12` of the OSX SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on OSX. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g.
cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
### OpenSSL
@ -28,18 +30,26 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
### Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. You will need to select the Xcode installation in the terminal first if you have not done so already.
You can ask CMake to generate Xcode project files instead of Unix Makefiles using the `-G Xcode` parameter after CMake. You will need to select the Xcode installation in the terminal first if you have not done so already.
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
cmake .. -G Xcode
cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally instaled CMake
After running cmake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run.
After running CMake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run.
If the build completes successfully, you will have built targets for all components located in the `build/${target_name}/Debug` directories.
### make
If you build with make rather than Xcode, you can append `-j4`for assigning more threads. The number indicates the number of threads, e.g. 4.
If you build with make rather than Xcode, you can append `-j4` for assigning more threads. The number indicates the number of threads, e.g. 4.
To package the installation, you can simply run `make package` afterwards.
### FAQ
1. **Problem:** Running the scheme `interface.app` from Xcode causes a crash for Interface related to `libgl`
1. **Cause:** The target `gl` generates a binary called `libgl`. A macOS `libGL.framework` item gets loaded instead by Xcode.
1. **Solution:** In the Xcode target settings for `libgl`, set the version to 1.0.0

View file

@ -120,7 +120,7 @@ else()
endif()
# Use default time server if none defined in environment
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://timestamp.comodoca.com?td=sha256")
set(HIFI_USE_OPTIMIZED_IK_OPTION OFF)
set(BUILD_CLIENT_OPTION ON)

View file

@ -1,5 +1,7 @@
# Creating an Installer
*Last Updated on August 24, 2020*
Follow the [build guide](BUILD.md) to figure out how to build Vircadia for your platform.
During generation, CMake should produce an `install` target and a `package` target.
@ -13,6 +15,8 @@ To produce an installer, run the `package` target. However you will want to foll
#### Windows
##### Prerequisites
To produce an executable installer on Windows, the following are required:
1. [7-zip](<https://www.7-zip.org/download.html>)
@ -59,6 +63,12 @@ To produce an executable installer on Windows, the following are required:
1. [Node.JS and NPM](<https://www.npmjs.com/get-npm>)
1. Install version 10.15.0 LTS
##### Code Signing (optional)
For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PASSPHRASE` environment variables to be present during CMake runtime and globally as we proceed to package the installer.
##### Creating the Installer
1. Perform a clean cmake from a new terminal.
1. Open the `vircadia.sln` solution with elevated (administrator) permissions on Visual Studio and select the **Release** configuration.
1. Build the solution.

View file

@ -9,7 +9,7 @@ import java.net.URISyntaxException;
public class HifiUtils {
public static final String METAVERSE_BASE_URL = "https://metaverse.highfidelity.com";
public static final String METAVERSE_BASE_URL = "https://metaverse.vircadia.com/live";
private static HifiUtils instance;

View file

@ -41,7 +41,7 @@ public class LoginFragment extends Fragment
private final String OAUTH_CLIENT_ID = BuildConfig.OAUTH_CLIENT_ID;
private final String OAUTH_REDIRECT_URI = BuildConfig.OAUTH_REDIRECT_URI;
private final String OAUTH_AUTHORIZE_BASE_URL = "https://highfidelity.com/oauth/authorize";
private final String OAUTH_AUTHORIZE_BASE_URL = "https://metaverse.vircadia.com/live/oauth/authorize";
private static final int OAUTH_AUTHORIZE_REQUEST = 1;
private EditText mUsername;
@ -222,7 +222,7 @@ public class LoginFragment extends Fragment
}
private void onForgotPasswordClicked() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://highfidelity.com/users/password/new"));
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://metaverse.vircadia.com/live/users/password/new"));
startActivity(intent);
}

View file

@ -28,7 +28,7 @@ import retrofit2.http.Query;
public class EndpointUsersProvider implements UsersProvider {
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
public static final String BASE_URL = "https://metaverse.vircadia.com/live/";
private final Retrofit mRetrofit;
private final EndpointUsersProviderService mEndpointUsersProviderService;
@ -154,18 +154,18 @@ public class EndpointUsersProvider implements UsersProvider {
}
public interface EndpointUsersProviderService {
@GET("api/v1/users")
@GET("/api/v1/users")
Call<UsersResponse> getUsers(@Query("filter") String filter,
@Query("per_page") int perPage,
@Query("online") Boolean online);
@DELETE("api/v1/user/connections/{connectionUserName}")
@DELETE("/api/v1/user/connections/{connectionUserName}")
Call<UsersResponse> removeConnection(@Path("connectionUserName") String connectionUserName);
@DELETE("api/v1/user/friends/{friendUserName}")
@DELETE("/api/v1/user/friends/{friendUserName}")
Call<UsersResponse> removeFriend(@Path("friendUserName") String friendUserName);
@POST("api/v1/user/friends")
@POST("/api/v1/user/friends")
Call<UsersResponse> addFriend(@Body BodyAddFriend friendUserName);
/* response

View file

@ -22,7 +22,7 @@ import retrofit2.http.Query;
public class UserStoryDomainProvider implements DomainProvider {
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
public static final String BASE_URL = "https://metaverse.vircadia.com/live/";
private static final String INCLUDE_ACTIONS_FOR_PLACES = "concurrency";
private static final String INCLUDE_ACTIONS_FOR_FULL_SEARCH = "concurrency,announcements,snapshot";
@ -169,7 +169,7 @@ public class UserStoryDomainProvider implements DomainProvider {
}
public interface UserStoryDomainProviderService {
@GET("api/v1/user_stories")
@GET("/api/v1/user_stories")
Call<UserStories> getUserStories(@Query("include_actions") String includeActions,
@Query("restriction") String restriction,
@Query("require_online") boolean requireOnline,

View file

@ -15,8 +15,8 @@ import io.highfidelity.hifiinterface.HifiUtils;
* available in an API
*/
public class DownloadProfileImageTask extends AsyncTask<String, Void, String> {
private static final String BASE_PROFILE_URL = "https://highfidelity.com";
// Note: This should now be available in the API, correct?
private static final String BASE_PROFILE_URL = "https://metaverse.vircadia.com/live";
private static final String TAG = "Interface";
private final DownloadProfileImageResultProcessor mResultProcessor;

View file

@ -48,7 +48,7 @@ ext {
def appDir = new File(projectDir, 'apps/interface')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
def baseUrl = 'https://cdn-1.vircadia.com/eu-c-1/vircadia-public/dependencies/android/'
def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms")
task extractGvrBinaries() {

View file

@ -11,7 +11,7 @@ buildscript {
def file='gvrsdk_v1.101.0.tgz'
def url='https://hifi-public.s3.amazonaws.com/austin/android/' + file
def url='https://cdn-1.vircadia.com/eu-c-1/vircadia-public/austin/android/' + file
def destFile = new File(HIFI_ANDROID_PRECOMPILED, file)
// FIXME find a way to only download if the file doesn't exist

View file

@ -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

View file

@ -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() {

View file

@ -183,7 +183,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
}
QString assignmentServerHostname;
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
}
if (parser.isSet(assignmentServerHostnameOption)) {
@ -192,7 +192,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
// check for an overriden assignment server port
quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION)) {
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toUInt();
}

View file

@ -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

View file

@ -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();
}

View file

@ -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);

View file

@ -14,7 +14,10 @@
#include <queue>
#if !defined(Q_MOC_RUN)
// Work around https://bugreports.qt.io/browse/QTBUG-80990
#include <tbb/concurrent_vector.h>
#endif
#include <QtCore/QJsonObject>

View file

@ -12,7 +12,10 @@
#ifndef hifi_AudioMixerSlave_h
#define hifi_AudioMixerSlave_h
#if !defined(Q_MOC_RUN)
// Work around https://bugreports.qt.io/browse/QTBUG-80990
#include <tbb/concurrent_vector.h>
#endif
#include <AABox.h>
#include <AudioHRTF.h>

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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";

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@
#
# Created by Leonardo Murillo on 07/14/2015.
# Copyright 2015 High Fidelity, Inc.
# Copyright 2020 Vircadia contributors.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -23,6 +24,7 @@ macro(SET_PACKAGING_PARAMETERS)
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
set_from_env(STABLE_BUILD STABLE_BUILD 0)
set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "")
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")
@ -174,7 +176,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(INTERFACE_SHORTCUT_NAME "Vircadia")
set(CONSOLE_SHORTCUT_NAME "Console")
set(SANDBOX_SHORTCUT_NAME "Sandbox")
set(APP_USER_MODEL_ID "com.highfidelity.console")
set(APP_USER_MODEL_ID "com.vircadia.console")
else ()
set(INTERFACE_SHORTCUT_NAME "Vircadia - ${BUILD_VERSION_NO_SHA}")
set(CONSOLE_SHORTCUT_NAME "Console - ${BUILD_VERSION_NO_SHA}")

View file

@ -22,11 +22,12 @@ namespace BuildInfo {
const QString DOMAIN_SERVER_NAME = "domain-server";
const QString AC_CLIENT_SERVER_NAME = "ac-client";
const QString MODIFIED_ORGANIZATION = "@BUILD_ORGANIZATION@";
const QString ORGANIZATION_DOMAIN = "highfidelity.io";
const QString ORGANIZATION_DOMAIN = "vircadia.com";
const QString VERSION = "@BUILD_VERSION@";
const QString BUILD_NUMBER = "@BUILD_NUMBER@";
const QString BUILD_GLOBAL_SERVICES = "@BUILD_GLOBAL_SERVICES@";
const QString BUILD_TIME = "@BUILD_TIME@";
const QString INITIAL_STARTUP_LOCATION = "@INITIAL_STARTUP_LOCATION@";
enum BuildType {
Dev,

View file

@ -2,18 +2,19 @@
# CPackProperties.cmake.in
# cmake/templates
#
# Copyright 2016 High Fidelity, Inc.
# Created by Stephen Birarda on January 11, 2016
# Copyright 2016 High Fidelity, Inc.
# Copyright 2020 Vircadia contributors.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
set(INTERFACE_DISPLAY_NAME "Interface")
set(INTERFACE_DISPLAY_NAME "Vircadia")
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
set(CONSOLE_DISPLAY_NAME "Console")
set(CONSOLE_DISPLAY_NAME "Vircadia Server Console")
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
@ -22,9 +23,9 @@ set(SANDBOX_HF_SHORTCUT_NAME "@SANDBOX_HF_SHORTCUT_NAME@")
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@")
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@")
set(DS_DISPLAY_NAME "Domain Server")
set(DS_DISPLAY_NAME "Vircadia Domain Server")
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
set(AC_DISPLAY_NAME "Assignment Client")
set(AC_DISPLAY_NAME "Vircadia Assignment Client")
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")
set(HIGH_FIDELITY_APP_PROTOCOL "@HIGH_FIDELITY_APP_PROTOCOL@")

View file

@ -199,18 +199,15 @@
!system "$%TEMP%\tempinstaller.exe" = 2
; NOTE: We're not code signing right now, so we're going to disable that.
; TODO: Get a code signing certificate so we can re-enable code signing.
; The Inner invocation has written an uninstaller binary for us.
; We need to sign it if it's a production or PR build.
; !if @PRODUCTION_BUILD@ == 1
; !if @BYPASS_SIGNING@ == 1
; !warning "BYPASS_SIGNING set - installer will not be signed"
; !else
; !system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
; !endif
; !endif
!if @PRODUCTION_BUILD@ == 1
!if @BYPASS_SIGNING@ == TRUE
!warning "BYPASS_SIGNING set - installer will not be signed"
!else
!system '"@SIGNTOOL_EXECUTABLE@" sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://timestamp.comodoca.com?td=sha256 /td SHA256 $%TEMP%\@UNINSTALLER_NAME@' = 0
!endif
!endif
; Good. Now we can carry on writing the real installer.
@ -639,6 +636,7 @@ Var Express
; figure out where to download installer slideshow images from
StrCpy $0 "http://cdn.highfidelity.com/installer/slideshow"
; Is this in use anymore?
${If} $CampaignName == ""
StrCpy $0 "$0/default"
@ -1303,13 +1301,13 @@ Section "-Core installation"
${If} @SERVER_COMPONENT_CONDITIONAL@
; handling for server console shortcut
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk"
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk" \
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" \
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; Set appUserModelId
ApplicationID::Set "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
ApplicationID::Set "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
; handling for interface only console shortcut
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; Set appUserModelId
@ -1520,7 +1518,7 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk"
Delete "$DESKTOP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
@ -1561,7 +1559,7 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@INTERFACE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@CONSOLE_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_SHORTCUT_NAME@.lnk"
Delete "$SMPROGRAMS\$MUI_TEMP\@SANDBOX_HF_SHORTCUT_NAME@.lnk"
@CPACK_NSIS_DELETE_ICONS_EXTRA@
;Delete empty start menu parent diretories

2
debian/rules vendored
View file

@ -6,7 +6,7 @@
override_dh_auto_configure:
mkdir obj-$(DEB_TARGET_MULTIARCH)
(cd obj-$(DEB_TARGET_MULTIARCH) && cmake .. -DCMAKE_INSTALL_PREFIX=/opt/hifi -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON "-GUnix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCLIENT_ONLY=ON -DDOWNLOAD_SERVERLESS_CONTENT=ON -DCMAKE_CXX_COMPILER=/usr/lib/llvm-7/bin/clang\+\+ -DOpenGL_GL_PREFERENCE=GLVND)
(cd obj-$(DEB_TARGET_MULTIARCH) && cmake .. -DCMAKE_INSTALL_PREFIX=/opt/hifi -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON "-GUnix Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCLIENT_ONLY=ON -DDOWNLOAD_SERVERLESS_CONTENT=ON -DCMAKE_CXX_COMPILER=/usr/lib/llvm-7/bin/clang\+\+ -DOpenGL_GL_PREFERENCE=LEGACY)
override_dh_auto_build:
(cd obj-$(DEB_TARGET_MULTIARCH) && make -j4)

View file

@ -8,20 +8,20 @@
{
"name": "access_token",
"label": "Access Token",
"help": "This is your OAuth access token to connect this domain-server with your High Fidelity account. <br/>It can be generated by clicking the 'Connect Account' button above.<br/>You can also go to the <a href='https://metaverse.highfidelity.com/user/security' target='_blank'>My Security</a> page of your account and generate a token with the 'domains' scope and paste it here.",
"help": "This is your OAuth access token to connect this domain-server with your Metaverse account. <br/>It can be generated by clicking the 'Connect Account' button above.<br/>You can also go to the Security page of your account on your Metaverse Server and generate a token with the 'domains' scope and paste it here.",
"advanced": true,
"backup": false
},
{
"name": "id",
"label": "Domain ID",
"help": "This is your High Fidelity domain ID. If you do not want your domain to be registered in the High Fidelity metaverse you can leave this blank.",
"help": "This is your Metaverse domain ID. If you do not want your domain to be registered in the Metaverse you can leave this blank.",
"advanced": true
},
{
"name": "automatic_networking",
"label": "Automatic Networking",
"help": "This defines how other nodes in the High Fidelity metaverse will be able to reach your domain-server.<br/>If you don't want to deal with any network settings, use full automatic networking.",
"help": "This defines how other nodes in the Metaverse will be able to reach your domain-server.<br/>If you don't want to deal with any network settings, use full automatic networking.",
"default": "disabled",
"type": "select",
"options": [
@ -35,7 +35,7 @@
},
{
"value": "disabled",
"label": "None: use the network information I have entered for this domain at metaverse.highfidelity.com"
"label": "None: use the network information I have entered for this domain at in the Metaverse Server."
}
]
},
@ -50,10 +50,26 @@
{
"name": "enable_packet_verification",
"label": "Enable Packet Verification",
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
"help": "Enable secure checksums on communication that uses the Metaverse protocol. Increases security with possibly a small performance penalty.",
"default": true,
"type": "checkbox",
"advanced": true
},
{
"name": "enable_metadata_exporter",
"label": "Enable Metadata HTTP Availability",
"help": "Allows your domain's metadata to be accessible on the public internet via direct HTTP connection to the domain server.",
"default": true,
"type": "checkbox",
"advanced": true
},
{
"name": "metadata_exporter_port",
"label": "Metadata Exporter HTTP Port",
"help": "This is the port where the Metaverse exporter accepts connections. It listens both on IPv4 and IPv6 and can be accessed remotely, so you should make sure to restrict access with a firewall as needed.",
"default": "9704",
"type": "int",
"advanced": true
}
]
},
@ -98,7 +114,7 @@
{
"name": "enable_prometheus_exporter",
"label": "Enable Prometheus Exporter",
"help": "Enable a Prometheus exporter to make it possible to gather the stats that are available at <a href='/'>Nodes</a> tab with a <a href='https://prometheus.io/'>Prometheus</a> server. This makes it possible to keep track of long-term domain statistics for graphing, troubleshooting, and performance monitoring.",
"help": "Enable a Prometheus exporter to make it possible to gather stats about the mixers that are available in the <a href='/'>Nodes</a> tab with a <a href='https://prometheus.io/'>Prometheus</a> server. This makes it possible to keep track of long-term domain statistics for graphing, troubleshooting, and performance monitoring.",
"default": false,
"type": "checkbox",
"advanced": true
@ -144,14 +160,42 @@
"name": "descriptors",
"label": "Description",
"restart": false,
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
"help": "This data will be queryable from your server. It may be collected by the Metaverse and used to share your domain with others.",
"settings": [
{
"name": "world_name",
"label": "Name",
"advanced": true,
"help": "The name of your domain (256 character limit)."
},
{
"name": "description",
"label": "Description",
"advanced": true,
"help": "A description of your domain (256 character limit)."
},
{
"name": "thumbnail",
"label": "World Thumbnail",
"advanced": true,
"help": "A link to the thumbnail that is publicly accessible from the internet."
},
{
"name": "images",
"label": "World Images",
"advanced": true,
"type": "table",
"can_add_new_rows": true,
"help": "URLs to images that visually describe your world to potential visitors.",
"numbered": false,
"columns": [
{
"name": "image",
"label": "Image URL",
"can_set": true
}
]
},
{
"name": "maturity",
"label": "Maturity",
@ -183,16 +227,22 @@
]
},
{
"name": "hosts",
"label": "Hosts",
"name": "contact_info",
"label": "World Administrative Contact",
"advanced": true,
"help": "Contact information to reach server administrators for assistance (256 character limit)."
},
{
"name": "managers",
"label": "World Managers / Administrators",
"advanced": true,
"type": "table",
"can_add_new_rows": true,
"help": "Usernames of hosts who can reliably show your domain to new visitors.",
"help": "Usernames of managers that administrate the domain.",
"numbered": false,
"columns": [
{
"name": "host",
"name": "manager",
"label": "Username",
"can_set": true
}
@ -243,6 +293,14 @@
"help": "Must match the password entered above for change to be saved.",
"value-hidden": true
},
{
"name": "approved_safe_urls",
"label": "Approved Script and QML URLs",
"help": "These URLs will be sent to the Interface as safe URLs to allow through the whitelist if the Interface has this security option enabled.",
"placeholder": "0",
"default": "1",
"advanced": false
},
{
"name": "maximum_user_capacity",
"label": "Maximum User Capacity",
@ -1830,7 +1888,7 @@
"name": "provider",
"label": "Provider",
"help": "OAuth provider URL.",
"default": "https://metaverse.highfidelity.com",
"default": "https://metaverse.vircadia.com/live",
"advanced": true,
"backup": false
},

View file

@ -0,0 +1,26 @@
<!--
//
// index.html
//
// Created by kasenvr@gmail.com on 21 Jul 2020
// Copyright 2020 Vircadia and contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-->
<html>
<head>
<title>Vircadia Metadata Exporter</title>
</head>
<body>
<h1>Vircadia Metadata Exporter</h1>
<p>If you can see this page, this means that your domain's metadata is available to be exported.</p>
<p>
<a href="/metadata">Metadata</a>
</p>
</body>
</html>

View file

@ -1,3 +1,3 @@
// Here you can put a script that will be run by an assignment-client (AC)
// For examples, please go to https://github.com/highfidelity/hifi/tree/master/script-archive/acScripts
// For examples, please go to https://github.com/kasenvr/project-athena/tree/master/script-archive/acScripts
// The directory named acScripts contains assignment-client specific scripts you can try.

View file

@ -51,9 +51,9 @@ $.extend(Settings, {
var URLs = {
// STABLE METAVERSE_URL: https://metaverse.highfidelity.com
// STAGING METAVERSE_URL: https://staging.highfidelity.com
METAVERSE_URL: 'https://metaverse.highfidelity.com',
CDN_URL: 'https://cdn.highfidelity.com',
PLACE_URL: 'https://hifi.place',
DEFAULT_METAVERSE_URL: "https://metaverse.vircadia.com/live",
CDN_URL: 'https://cdn-1.vircadia.com/eu-c-1',
PLACE_URL: 'https://xr.place'
};
var Strings = {
@ -61,7 +61,7 @@ var Strings = {
CHOOSE_DOMAIN_BUTTON: "Choose from my domains",
CREATE_DOMAIN_BUTTON: "Create new domain ID",
CREATE_DOMAIN_SUCCESS_JUST_CONNECTED: "We connnected your High Fidelity account and created a new domain ID for this machine.",
CREATE_DOMAIN_SUCCESS_JUST_CONNECTED: "We connnected your Metaverse account and created a new domain ID for this machine.",
CREATE_DOMAIN_SUCCESS: "We created a new domain ID for this machine.",
// When a place modification fails, they will be brought back to the previous
@ -92,7 +92,7 @@ var Strings = {
ADD_PLACE_LOADING_DIALOG: "Loading your places...",
ADD_PLACE_NOT_CONNECTED_TITLE: "Access token required",
ADD_PLACE_NOT_CONNECTED_MESSAGE: "You must have an access token to query your High Fidelity places.<br><br>Please follow the instructions on the settings page to add an access token.",
ADD_PLACE_NOT_CONNECTED_MESSAGE: "You must have an access token to query your Metaverse places.<br><br>Please follow the instructions on the settings page to add an access token.",
};
var DOMAIN_ID_TYPE_NONE = 0;
@ -228,215 +228,214 @@ function getDomainFromAPI(callback) {
return pendingDomainRequest;
}
function chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded) {
function chooseFromMetaversePlaces(accessToken, forcePathTo, onSuccessfullyAdded) {
if (accessToken) {
getMetaverseUrl(function(metaverse_url) {
var loadingDialog = showLoadingDialog(Strings.ADD_PLACE_LOADING_DIALOG);
var loadingDialog = showLoadingDialog(Strings.ADD_PLACE_LOADING_DIALOG);
function loadPlaces() {
$.ajax("/api/places", {
dataType: 'json',
jsonp: false,
success: function(data) {
if (data.status == 'success') {
var modal_buttons = {
cancel: {
label: Strings.ADD_PLACE_CANCEL_BUTTON,
className: 'add-place-cancel-button btn-default'
}
};
var dialog;
var modal_body;
if (data.data.places.length) {
var places_by_id = {};
modal_body = $('<div>');
modal_body.append($("<p>Choose a place name that you own or <a href='" + URLs.METAVERSE_URL + "/user/places' target='_blank'>register a new place name</a></p>"));
var currentDomainIDType = getCurrentDomainIDType();
if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) {
var warning = "<div class='domain-loading-error alert alert-warning'>";
warning += "If you choose a place name it will replace your current temporary place name.";
warning += "</div>";
modal_body.append(warning);
}
// setup a select box for the returned places
modal_body.append($("<label for='place-name-select'>Places</label>"));
place_select = $("<select id='place-name-select' class='form-control'></select>");
_.each(data.data.places, function(place) {
places_by_id[place.id] = place;
place_select.append("<option value='" + place.id + "'>" + place.name + "</option>");
})
modal_body.append(place_select);
modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>"));
if (forcePathTo === undefined || forcePathTo === null) {
var path = "<div class='form-group'>";
path += "<label for='place-path-input' class='control-label'>Path or Viewpoint</label>";
path += "<input type='text' id='place-path-input' class='form-control' value='/'>";
path += "</div>";
modal_body.append($(path));
}
var place_select = modal_body.find("#place-name-select")
place_select.change(function(ev) {
var warning = modal_body.find("#place-name-warning");
var place = places_by_id[$(this).val()];
if (place === undefined || place.pointee === null) {
warning.hide();
} else {
warning.show();
function loadPlaces() {
$.ajax("/api/places", {
dataType: 'json',
jsonp: false,
success: function(data) {
if (data.status == 'success') {
var modal_buttons = {
cancel: {
label: Strings.ADD_PLACE_CANCEL_BUTTON,
className: 'add-place-cancel-button btn-default'
}
});
place_select.trigger('change');
};
modal_buttons["success"] = {
label: Strings.ADD_PLACE_CONFIRM_BUTTON,
className: 'add-place-confirm-button btn btn-primary',
callback: function() {
var placeID = $('#place-name-select').val();
// set the place ID on the form
$(Settings.place_ID_SELECTOR).val(placeID).change();
var dialog;
var modal_body;
if (data.data.places.length) {
var places_by_id = {};
if (forcePathTo === undefined || forcePathTo === null) {
var placePath = $('#place-path-input').val();
modal_body = $('<div>');
modal_body.append($("<p>Choose a place name that you own or <a href='" + metaverse_url + "/user/places' target='_blank'>register a new place name</a></p>"));
var currentDomainIDType = getCurrentDomainIDType();
if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) {
var warning = "<div class='domain-loading-error alert alert-warning'>";
warning += "If you choose a place name it will replace your current temporary place name.";
warning += "</div>";
modal_body.append(warning);
}
// setup a select box for the returned places
modal_body.append($("<label for='place-name-select'>Places</label>"));
place_select = $("<select id='place-name-select' class='form-control'></select>");
_.each(data.data.places, function(place) {
places_by_id[place.id] = place;
place_select.append("<option value='" + place.id + "'>" + place.name + "</option>");
})
modal_body.append(place_select);
modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>"));
if (forcePathTo === undefined || forcePathTo === null) {
var path = "<div class='form-group'>";
path += "<label for='place-path-input' class='control-label'>Path or Viewpoint</label>";
path += "<input type='text' id='place-path-input' class='form-control' value='/'>";
path += "</div>";
modal_body.append($(path));
}
var place_select = modal_body.find("#place-name-select")
place_select.change(function(ev) {
var warning = modal_body.find("#place-name-warning");
var place = places_by_id[$(this).val()];
if (place === undefined || place.pointee === null) {
warning.hide();
} else {
var placePath = forcePathTo;
warning.show();
}
});
place_select.trigger('change');
$('.add-place-confirm-button').attr('disabled', 'disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING);
$('.add-place-cancel-button').attr('disabled', 'disabled');
modal_buttons["success"] = {
label: Strings.ADD_PLACE_CONFIRM_BUTTON,
className: 'add-place-confirm-button btn btn-primary',
callback: function() {
var placeID = $('#place-name-select').val();
// set the place ID on the form
$(Settings.place_ID_SELECTOR).val(placeID).change();
function finalizeSaveDomainID(domainID) {
var jsonSettings = {
metaverse: {
id: domainID
}
}
var dialog = showLoadingDialog("Waiting for Domain Server to restart...");
$.ajax('/settings.json', {
data: JSON.stringify(jsonSettings),
contentType: 'application/json',
type: 'POST'
}).done(function(data) {
if (data.status == "success") {
waitForDomainServerRestart(function() {
dialog.modal('hide');
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name, domainID);
}
});
} else {
bootbox.alert("Failed to add place");
}
}).fail(function() {
bootbox.alert("Failed to add place");
});
}
// If domainID is not specified, the current domain id will be used.
function finishSettingUpPlace(domainID) {
sendUpdatePlaceRequest(
placeID,
placePath,
domainID,
false,
function(data) {
dialog.modal('hide')
if (domainID) {
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
finalizeSaveDomainID(domainID);
} else {
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name);
}
}
},
function(data) {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
}
);
}
function maybeCreateNewDomainID() {
console.log("Maybe creating domain id", currentDomainIDType)
if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) {
finishSettingUpPlace();
if (forcePathTo === undefined || forcePathTo === null) {
var placePath = $('#place-path-input').val();
} else {
sendCreateDomainRequest(function(domainID) {
console.log("Created domain", domainID);
finishSettingUpPlace(domainID);
}, function() {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
var placePath = forcePathTo;
}
$('.add-place-confirm-button').attr('disabled', 'disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING);
$('.add-place-cancel-button').attr('disabled', 'disabled');
function finalizeSaveDomainID(domainID) {
var jsonSettings = {
metaverse: {
id: domainID
}
}
var dialog = showLoadingDialog("Waiting for Domain Server to restart...");
$.ajax('/settings.json', {
data: JSON.stringify(jsonSettings),
contentType: 'application/json',
type: 'POST'
}).done(function(data) {
if (data.status == "success") {
waitForDomainServerRestart(function() {
dialog.modal('hide');
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name, domainID);
}
});
} else {
bootbox.alert("Failed to add place");
}
}).fail(function() {
bootbox.alert("Failed to add place");
});
}
// If domainID is not specified, the current domain id will be used.
function finishSettingUpPlace(domainID) {
sendUpdatePlaceRequest(
placeID,
placePath,
domainID,
false,
function(data) {
dialog.modal('hide')
if (domainID) {
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
finalizeSaveDomainID(domainID);
} else {
if (onSuccessfullyAdded) {
onSuccessfullyAdded(places_by_id[placeID].name);
}
}
},
function(data) {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
}
);
}
function maybeCreateNewDomainID() {
console.log("Maybe creating domain id", currentDomainIDType)
if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) {
finishSettingUpPlace();
} else {
sendCreateDomainRequest(function(domainID) {
console.log("Created domain", domainID);
finishSettingUpPlace(domainID);
}, function() {
$('.add-place-confirm-button').removeAttr('disabled');
$('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON);
$('.add-place-cancel-button').removeAttr('disabled');
bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR);
});
}
}
maybeCreateNewDomainID();
return false;
}
maybeCreateNewDomainID();
return false;
}
} else {
modal_buttons["success"] = {
label: Strings.ADD_PLACE_NO_PLACES_BUTTON,
callback: function() {
window.open(metaverse_url + "/user/places", '_blank');
}
}
modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE;
}
dialog = bootbox.dialog({
title: Strings.ADD_PLACE_TITLE,
message: modal_body,
closeButton: false,
buttons: modal_buttons,
onEscape: true
});
} else {
modal_buttons["success"] = {
label: Strings.ADD_PLACE_NO_PLACES_BUTTON,
callback: function() {
window.open(URLs.METAVERSE_URL + "/user/places", '_blank');
}
}
modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE;
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
}
dialog = bootbox.dialog({
title: Strings.ADD_PLACE_TITLE,
message: modal_body,
closeButton: false,
buttons: modal_buttons,
onEscape: true
});
} else {
},
error: function() {
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
},
complete: function() {
loadingDialog.modal('hide');
}
},
error: function() {
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
},
complete: function() {
loadingDialog.modal('hide');
}
});
}
var domainType = getCurrentDomainIDType();
if (domainType !== DOMAIN_ID_TYPE_UNKNOWN) {
loadPlaces();
} else {
getDomainFromAPI(function(data) {
if (data.status === 'success') {
var domainType = getCurrentDomainIDType();
loadPlaces();
} else {
loadingDialog.modal('hide');
bootbox.confirm("We were not able to load your domain information from the Metaverse. Would you like to retry?", function(response) {
if (response) {
chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded);
}
});
}
})
}
});
}
var domainType = getCurrentDomainIDType();
if (domainType !== DOMAIN_ID_TYPE_UNKNOWN) {
loadPlaces();
} else {
getDomainFromAPI(function(data) {
if (data.status === 'success') {
var domainType = getCurrentDomainIDType();
loadPlaces();
} else {
loadingDialog.modal('hide');
bootbox.confirm("We were not able to load your domain information from the Metaverse. Would you like to retry?", function(response) {
if (response) {
chooseFromMetaversePlaces(accessToken, forcePathTo, onSuccessfullyAdded);
}
});
}
})
}
});
} else {
bootbox.alert({
title: Strings.ADD_PLACE_NOT_CONNECTED_TITLE,
@ -501,14 +500,38 @@ 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) {
callback(data.metaverse_url);
},
error: function() {
callback(URLs.METAVERSE_URL);
callback(URLs.DEFAULT_METAVERSE_URL);
}
});
}

View file

@ -16,7 +16,7 @@ $(document).ready(function(){
Settings.extraGroupsAtEnd = Settings.extraDomainGroupsAtEnd;
Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex;
var METAVERSE_URL = URLs.METAVERSE_URL;
var METAVERSE_URL = URLs.DEFAULT_METAVERSE_URL;
var SSL_PRIVATE_KEY_FILE_ID = 'ssl-private-key-file';
var SSL_PRIVATE_KEY_CONTENTS_ID = 'key-contents';
@ -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();
@ -277,7 +279,7 @@ $(document).ready(function(){
swal({
title: '',
type: 'error',
text: "There was a problem retrieving domain information from High Fidelity API.",
text: "There was a problem retrieving domain information from the Metaverse API.",
confirmButtonText: 'Try again',
showCancelButton: true,
closeOnConfirm: false
@ -306,7 +308,7 @@ $(document).ready(function(){
if (hasAccessToken) {
el = "<p>";
el += "<span class='account-connected-header'>High Fidelity Account Connected</span>";
el += "<span class='account-connected-header'>Metaverse Account Connected</span>";
el += "<button id='" + Settings.DISCONNECT_ACCOUNT_BTN_ID + "' class='btn'>Disconnect</button>";
el += "</p>";
el = $(el);
@ -319,7 +321,7 @@ $(document).ready(function(){
}
buttonSetting.help = "";
buttonSetting.classes = "btn-primary";
buttonSetting.button_label = "Connect High Fidelity Account";
buttonSetting.button_label = "Connect Metaverse Account";
buttonSetting.html_id = Settings.CONNECT_ACCOUNT_BTN_ID;
buttonSetting.href = METAVERSE_URL + "/user/tokens/new?for_domain_server=true";
@ -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,9 +387,14 @@ $(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.id;
var domainID = data.domain.domainId;
console.log("Setting domain id to ", data, domainID);
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
@ -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,9 +728,9 @@ $(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> "
+ "page in your High Fidelity Metaverse account.",
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,
columns: [
@ -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();
}
@ -853,7 +871,7 @@ $(document).ready(function(){
}
// Update label
if (showOrHideLabel()) {
var label = data.domain.label;
var label = data.domain.name;
label = label === null ? '' : label;
$('#network-label').val(label);
}
@ -959,7 +977,7 @@ $(document).ready(function(){
var addRow = $("<tr> <td></td> <td></td> <td class='buttons'><a href='#' class='place-add glyphicon glyphicon-plus'></a></td> </tr>");
addRow.find(".place-add").click(function(ev) {
ev.preventDefault();
chooseFromHighFidelityPlaces(Settings.initialValues.metaverse.access_token, null, function(placeName, newDomainID) {
chooseFromMetaversePlaces(Settings.initialValues.metaverse.access_token, null, function(placeName, newDomainID) {
if (newDomainID) {
Settings.data.values.metaverse.id = newDomainID;
var domainIDEl = $("[data-keypath='metaverse.id']");
@ -997,18 +1015,18 @@ $(document).ready(function(){
if (data.data.domains.length) {
// setup a select box for the returned domains
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>";
modal_body = "<p>Choose the Metaverse domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>";
domain_select = $("<select id='domain-name-select' class='form-control'></select>");
_.each(data.data.domains, function(domain){
var domainString = "";
if (domain.label) {
domainString += '"' + domain.label+ '" - ';
if (domain.name) {
domainString += '"' + domain.name+ '" - ';
}
domainString += domain.id;
domainString += domain.domainId;
domain_select.append("<option value='" + domain.id + "'>" + domainString + "</option>");
domain_select.append("<option value='" + domain.domainId + "'>" + domainString + "</option>");
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {
@ -1026,7 +1044,7 @@ $(document).ready(function(){
window.open(METAVERSE_URL + "/user/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your High Fidelity account." +
modal_body = "<p>You do not have any domains in your Metaverse account." +
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
}
@ -1038,7 +1056,7 @@ $(document).ready(function(){
})
},
error: function() {
bootbox.alert("Failed to retrieve your domains from the High Fidelity Metaverse");
bootbox.alert("Failed to retrieve your domains from the Metaverse");
},
complete: function() {
// remove the spinner from the choose button
@ -1049,7 +1067,7 @@ $(document).ready(function(){
} else {
bootbox.alert({
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
message: "You must have an access token to query your Metaverse domains.<br><br>" +
"Please follow the instructions on the settings page to add an access token.",
title: "Access token required"
})

View file

@ -3,12 +3,12 @@
<h4 class="step-title"></h4>
<dl class="row">
<dd class="col-md-12">
<span class='step-description'>By connecting your High Fidelity Account you will be granting access to your account information.</span>
<span class='step-description'>By connecting your Metaverse Account you will be granting access to your account information.</span>
</dd>
</dl>
<dl class="row">
<dd class="col-md-12">
<a id="connect-account-btn" role="button" class="btn btn-primary btn-md btn-block" target="_blank">Connect your High Fidelity account</a>
<a id="connect-account-btn" role="button" class="btn btn-primary btn-md btn-block" target="_blank">Connect your Metaverse account</a>
</dd>
</dl>
@ -19,13 +19,13 @@
</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">
<span class='step-description'>
<a target='_blank' href='https://docs.vircadia.dev/create-and-explore/start-working-in-your-sandbox/place-names'>Place names</a> are similar to web addresses. Users who want to visit your domain can
enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.</br>
enter its Place Name in Vircadia's Interface. You can choose a Place Name for your domain.</br>
Your domain may also be reachable by <b>IP address</b>.
</span>
</div>
@ -51,16 +51,16 @@
<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>
<div class="row">
<p id="permissions-description" class="col-md-12 step-info"><b>Localhost</b> has been granted administrator privileges to this domain. (Localhost is any</br>user on the same machine as the High Fidelity Server)</p>
<p id="permissions-description" class="col-md-12 step-info"><b>Localhost</b> has been granted administrator privileges to this domain. (Localhost is any</br>user on the same machine as the Vircadia server)</p>
</div>
<div id="admin-row" class="row">
<p class="col-md-6">
<span class="step-info"><span id="admin-description"><b>Add your High Fidelity username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
<span class="step-info"><span id="admin-description"><b>Add your Metaverse username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
</p>
<div class="col-md-6">
<input id="admin-usernames" type="text" class="form-control">
@ -95,7 +95,7 @@
</p>
<p class="col-md-5">
<label>
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into High Fidelity
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into the Metaverse
</label>
</p>
<p class="col-md-2">
@ -126,7 +126,7 @@
</p>
<p class="col-md-5">
<label>
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into High Fidelity
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into the Metaverse
</label>
</p>
<p class="col-md-2">
@ -164,14 +164,14 @@
<dt class="col-md-4 step-info">Username</dt>
<dd class="col-md-8">
<input id="http_username" type="text" class="form-control">
<span class='help-block'>This does not have to be your High Fidelity username</span>
<span class='help-block'>This does not have to be your Metaverse username</span>
</dd>
</dl>
<dl class="row">
<dt class="col-md-4 step-info">Enter password</dt>
<dd class="col-md-8">
<input id="http_password" type="password" class="form-control">
<span class='help-block'>This should not be the same as your High Fidelity password</span>
<span class='help-block'>This should not be the same as your Metaverse password</span>
</dd>
</dl>
<dl id="verify-password-row" class="row">
@ -186,7 +186,37 @@
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
</dd>
<dd class="col-md-3 col-md-offset-6">
<button id="save-username-password" type="button" class="btn btn-md btn-block btn-primary">Finish</button>
<button id="save-username-password" type="button" class="btn btn-md btn-block btn-primary next-button">Next</button>
</dd>
</dl>
</div>
<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">
<span class='step-description'>
Would you like use automatic threading for your server's avatars and audio?
If you are hosting this server on your local computer and it is not very powerful, then consider leaving this off because the server will use more resources if it is being utilized extensively, thereby slowing down your computer.
If you are running this server on a powerful system and intend to have a large audience, then turn this setting on.
</span>
</div>
</div>
<br />
<div id="automatic-threading-options-row" class="row">
<p class="col-md-12">
<label>
<input id="enable-automatic-threading" name="threading-checkbox" type="checkbox" value="true"> Enable Automatic Threading
</label>
</p>
</div>
<dl class="row">
<dd class="col-md-3">
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
</dd>
<dd class="col-md-3 col-md-offset-6">
<button id="save-threading-settings" type="button" class="btn btn-md btn-block btn-primary">Finish</button>
</dd>
</dl>
</div>
@ -201,7 +231,7 @@
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<p id="main-description" class="step-info">You have successfully setup and configured your cloud hosted domain.</p>
<p id="main-description" class="step-info">You have successfully setup and configured your Vircadia domain.</p>
</div>
</div>
@ -262,4 +292,5 @@
<script src='/js/bootbox.min.js'></script>
<script src='/js/sha256.js'></script>
<script src='js/wizard.js'></script>
<script src='../js/shared.js'></script>
<!--#include virtual="page-end.html"-->

View file

@ -2,121 +2,132 @@ var Metaverse = {
accessToken: null
}
var CURRENT_METAVERSE_URL;
var currentStepNumber;
$(document).ready(function(){
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.<br><br>" +
"Please go back and connect your account.";
getMetaverseUrl(function(metaverse_url) {
CURRENT_METAVERSE_URL = metaverse_url;
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your Metaverse places.<br><br>" +
"Please go back and connect your account.";
$('[data-toggle="tooltip"]').tooltip();
$('#connect-account-btn').attr('href', CURRENT_METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
$('.perms-link').on('click', function() {
var modal_body = '<div>';
modal_body += '<b>None</b> - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.</br></br>';
modal_body += '<b>Friends</b> - Users who are your Friends in High Fidelity.</br></br>';
modal_body += '<b>Users logged into High Fidelity</b> - Users who are currently logged into High Fidelity.</br></br>';
modal_body += '<b>Everyone</b> - Anyone who uses High Fidelity.';
modal_body += '</div>';
$('[data-toggle="tooltip"]').tooltip();
dialog = bootbox.dialog({
title: "User definition",
message: modal_body,
closeButton: true
$('.perms-link').on('click', function() {
var modal_body = '<div>';
modal_body += '<b>None</b> - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.</br></br>';
modal_body += '<b>Friends</b> - Users who are your Friends in the Metaverse.</br></br>';
modal_body += '<b>Users logged into the Metaverse</b> - Users who are currently logged into the Metaverse.</br></br>';
modal_body += '<b>Everyone</b> - Anyone who uses the Metaverse.';
modal_body += '</div>';
dialog = bootbox.dialog({
title: "User definition",
message: modal_body,
closeButton: true
});
return false;
});
return false;
});
$('body').on('click', '.next-button', function() {
goToNextStep();
});
$('body').on('click', '.back-button', function() {
goToPreviousStep();
});
$('body').on('click', '#skip-wizard-button', function() {
skipWizard();
})
$('body').on('click', '#connect-account-btn', function() {
$(this).blur();
prepareAccessTokenPrompt(function(accessToken) {
Metaverse.accessToken = accessToken;
saveAccessToken();
$('body').on('click', '.next-button', function() {
goToNextStep();
});
});
$('body').on('click', '.back-button', function() {
goToPreviousStep();
});
$('body').on('click', '#skip-wizard-button', function() {
skipWizard();
})
$('body').on('click', '#connect-account-btn', function() {
$(this).blur();
prepareAccessTokenPrompt(function(accessToken) {
Metaverse.accessToken = accessToken;
saveAccessToken();
promptToCreateDomainID();
});
});
$('body').on('click', '#save-permissions', function() {
savePermissions();
});
function triggerSaveUsernamePassword(event) {
if (event.keyCode === 13) {
$("#save-username-password").click();
}
}
$("#http_username").keyup(triggerSaveUsernamePassword);
$("#http_password").keyup(triggerSaveUsernamePassword);
$("#verify_http_password").keyup(triggerSaveUsernamePassword);
$('body').on('click', '#save-username-password', function() {
saveUsernamePassword();
$('body').on('click', '#save-threading-settings', function() {
saveThreadingSettings();
});
$('body').on('click', '#change-place-name', function() {
chooseFromHighFidelityPlaces(Settings.data.values.metaverse.access_token, "/0,-10,0", function(placeName) {
updatePlaceNameLink(placeName);
});
});
$('body').on('click', '#visit-domain', function() {
$('#share-link')[0].click();
});
$('input[type=radio][name=connect-radio]').change(function() {
var inputs = $('input[type=radio][name=rez-radio]');
var disabled = [];
switch (this.value) {
case 'none':
disabled = inputs.splice(1);
break;
case 'friends':
disabled = inputs.splice(2);
break;
case 'logged-in':
disabled = inputs.splice(3);
break;
case 'everyone':
disabled = inputs.splice(4);
break;
function triggerSaveUsernamePassword(event) {
if (event.keyCode === 13) {
$("#save-username-password").click();
}
}
$.each(inputs, function() {
$(this).prop('disabled', false);
$("#http_username").keyup(triggerSaveUsernamePassword);
$("#http_password").keyup(triggerSaveUsernamePassword);
$("#verify_http_password").keyup(triggerSaveUsernamePassword);
$('body').on('click', '#save-username-password', function() {
saveUsernamePassword();
});
$.each(disabled, function() {
if ($(this).prop('checked')) {
$(inputs.last()).prop('checked', true);
}
$(this).prop('disabled', true);
});
});
reloadSettings(function(success) {
if (success) {
getDomainFromAPI();
setupWizardSteps();
updatePlaceNameDisplay();
updateUsernameDisplay();
} else {
swal({
title: '',
type: 'error',
text: "There was a problem loading the domain settings.\nPlease refresh the page to try again.",
$('body').on('click', '#change-place-name', function() {
chooseFromMetaversePlaces(Settings.data.values.metaverse.access_token, "/0,-10,0", function(placeName) {
updatePlaceNameLink(placeName);
});
}
});
$('body').on('click', '#visit-domain', function() {
$('#share-link')[0].click();
});
$('input[type=radio][name=connect-radio]').change(function() {
var inputs = $('input[type=radio][name=rez-radio]');
var disabled = [];
switch (this.value) {
case 'none':
disabled = inputs.splice(1);
break;
case 'friends':
disabled = inputs.splice(2);
break;
case 'logged-in':
disabled = inputs.splice(3);
break;
case 'everyone':
disabled = inputs.splice(4);
break;
}
$.each(inputs, function() {
$(this).prop('disabled', false);
});
$.each(disabled, function() {
if ($(this).prop('checked')) {
$(inputs.last()).prop('checked', true);
}
$(this).prop('disabled', true);
});
});
reloadSettings(function(success) {
if (success) {
getDomainFromAPI();
setupWizardSteps();
updatePlaceNameDisplay();
updateUsernameDisplay();
} else {
swal({
title: '',
type: 'error',
text: "There was a problem loading the domain settings.\nPlease refresh the page to try again.",
});
}
});
});
});
@ -142,10 +153,10 @@ function setupWizardSteps() {
});
$('#permissions-description').html('You <span id="username-display"></span>have been assigned administrator privileges to this domain.');
$('#admin-description').html('Add more High Fidelity usernames');
$('#admin-description').html('Add more Metaverse usernames');
} else {
$('.cloud-only').remove();
$('#save-permissions').text("Finish");
$('#save-threading-settings').text("Finish");
steps = $('.wizard-step');
$(steps).each(function(i) {
@ -161,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;
@ -171,7 +221,7 @@ function updatePlaceNameLink(address) {
function updatePlaceNameDisplay() {
if (Settings.data.values.metaverse.id) {
$.getJSON(URLs.METAVERSE_URL + '/api/v1/domains/' + Settings.data.values.metaverse.id, function(data) {
$.getJSON(CURRENT_METAVERSE_URL + '/api/v1/domains/' + Settings.data.values.metaverse.id, function(data) {
if (data.status === 'success') {
if (data.domain.default_place_name) {
@ -254,10 +304,6 @@ function goToNextStep() {
var currentStep = $('body').find('.wizard-step:visible');
var nextStep = currentStep.next('.wizard-step');
var formJSON = {
"wizard": {}
}
if (nextStep.length > 0) {
currentStep.hide();
nextStep.show();
@ -335,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) {
@ -515,7 +561,29 @@ function saveUsernamePassword() {
$(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
postSettings(formJSON, function() {
location.reload();
});
postSettings(formJSON, goToNextStep);
}
function saveThreadingSettings() {
var enable_automatic_threading = $("#enable-automatic-threading").prop("checked");
currentStepNumber += 1;
var formJSON = {
"audio_threading": {
"auto_threads": enable_automatic_threading
},
"avatar_mixer": {
"auto_threads": enable_automatic_threading
},
"wizard": {
"steps_completed": currentStepNumber.toString()
}
}
// remove focus from the button
$(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
postSettings(formJSON, goToNextStep);
}

View file

@ -17,7 +17,8 @@
#include <openssl/x509.h>
#include <random>
#include <QDataStream>
#include <QtCore/QDataStream>
#include <QtCore/QMetaMethod>
#include <AccountManager.h>
#include <Assignment.h>
@ -821,7 +822,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username, bool isOpti
callbackParams.errorCallbackMethod = "publicKeyJSONErrorCallback";
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
const QString USER_PUBLIC_KEY_PATH = "/api/v1/users/%1/public_key";
qDebug().nospace() << "Requesting " << (isOptimistic ? "optimistic " : " ") << "public key for user " << username;
@ -1048,7 +1049,7 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
callbackParams.jsonCallbackMethod = "getIsGroupMemberJSONCallback";
callbackParams.errorCallbackMethod = "getIsGroupMemberErrorCallback";
const QString GET_IS_GROUP_MEMBER_PATH = "api/v1/groups/members/%2";
const QString GET_IS_GROUP_MEMBER_PATH = "/api/v1/groups/members/%2";
DependencyManager::get<AccountManager>()->sendRequest(GET_IS_GROUP_MEMBER_PATH.arg(username),
AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation, callbackParams,
@ -1114,7 +1115,7 @@ void DomainGatekeeper::getDomainOwnerFriendsList() {
callbackParams.jsonCallbackMethod = "getDomainOwnerFriendsListJSONCallback";
callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback";
const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends";
const QString GET_FRIENDS_LIST_PATH = "/api/v1/user/friends";
if (DependencyManager::get<AccountManager>()->hasValidAccessToken()) {
DependencyManager::get<AccountManager>()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::GetOperation, callbackParams, QByteArray(),
@ -1196,7 +1197,7 @@ Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) {
return existingLocalIDIt->second;
}
assert(_localIDs.size() < std::numeric_limits<LocalIDs::value_type>::max() - 2);
assert(_localIDs.size() < (size_t)(std::numeric_limits<LocalIDs::value_type>::max() - 2));
Node::LocalID newLocalID;
do {

View file

@ -4,20 +4,25 @@
//
// Created by Zach Pomerantz on 5/25/2016.
// Copyright 2016 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "DomainMetadata.h"
#include "HTTPConnection.h"
#include <AccountManager.h>
#include <DependencyManager.h>
#include <HifiConfigVariantMap.h>
#include <LimitedNodeList.h>
#include <QLoggingCategory>
#include "DomainServer.h"
#include "DomainServerNodeData.h"
Q_LOGGING_CATEGORY(domain_metadata_exporter, "hifi.domain_server.metadata_exporter")
const QString DomainMetadata::USERS = "users";
const QString DomainMetadata::Users::NUM_TOTAL = "num_users";
const QString DomainMetadata::Users::NUM_ANON = "num_anon_users";
@ -29,18 +34,28 @@ const QString DomainMetadata::Users::HOSTNAMES = "user_hostnames";
// }
const QString DomainMetadata::DESCRIPTORS = "descriptors";
const QString DomainMetadata::Descriptors::NAME = "world_name";
const QString DomainMetadata::Descriptors::DESCRIPTION = "description";
const QString DomainMetadata::Descriptors::THUMBNAIL = "thumbnail";
const QString DomainMetadata::Descriptors::IMAGES = "images";
const QString DomainMetadata::Descriptors::CAPACITY = "capacity"; // parsed from security
const QString DomainMetadata::Descriptors::RESTRICTION = "restriction"; // parsed from ACL
const QString DomainMetadata::Descriptors::MATURITY = "maturity";
const QString DomainMetadata::Descriptors::HOSTS = "hosts";
const QString DomainMetadata::Descriptors::CONTACT = "contact_info";
const QString DomainMetadata::Descriptors::MANAGERS = "managers";
const QString DomainMetadata::Descriptors::TAGS = "tags";
// descriptors metadata will appear as (JSON):
// { "description": String, // capped description
// {
// "world_name": String, // capped name
// "description": String, // capped description
// "thumbnail": String, // capped thumbnail URL
// "images": [ String ], // capped list of image URLs
// "capacity": Number,
// "restriction": String, // enum of either open, hifi, or acl
// "maturity": String, // enum corresponding to ESRB ratings
// "hosts": [ String ], // capped list of usernames
// "contact_info": [ String ], // capped list of usernames
// "managers": [ String ], // capped list of usernames
// "tags": [ String ], // capped list of tags
// }
@ -54,17 +69,6 @@ DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
_metadata[USERS] = QVariantMap {};
_metadata[DESCRIPTORS] = QVariantMap {};
assert(dynamic_cast<DomainServer*>(domainServer));
DomainServer* server = static_cast<DomainServer*>(domainServer);
// update the metadata when a user (dis)connects
connect(server, &DomainServer::userConnected, this, &DomainMetadata::usersChanged);
connect(server, &DomainServer::userDisconnected, this, &DomainMetadata::usersChanged);
// update the metadata when security changes
connect(&server->_settingsManager, &DomainServerSettingsManager::updateNodePermissions,
this, static_cast<void(DomainMetadata::*)()>(&DomainMetadata::securityChanged));
// initialize the descriptors
securityChanged(false);
descriptorsChanged();
@ -88,12 +92,40 @@ void DomainMetadata::descriptorsChanged() {
static const QString DESCRIPTORS_GROUP_KEYPATH = "descriptors";
auto descriptorsMap = static_cast<DomainServer*>(parent())->_settingsManager.valueForKeyPath(DESCRIPTORS).toMap();
// copy simple descriptors (description/maturity)
state[Descriptors::DESCRIPTION] = descriptorsMap[Descriptors::DESCRIPTION];
state[Descriptors::MATURITY] = descriptorsMap[Descriptors::MATURITY];
// copy simple descriptors
if (!descriptorsMap[Descriptors::NAME].isNull()) {
state[Descriptors::NAME] = descriptorsMap[Descriptors::NAME];
} else {
state[Descriptors::NAME] = "";
}
// copy array descriptors (hosts/tags)
state[Descriptors::HOSTS] = descriptorsMap[Descriptors::HOSTS].toList();
if (!descriptorsMap[Descriptors::DESCRIPTION].isNull()) {
state[Descriptors::DESCRIPTION] = descriptorsMap[Descriptors::DESCRIPTION];
} else {
state[Descriptors::DESCRIPTION] = "";
}
if (!descriptorsMap[Descriptors::THUMBNAIL].isNull()) {
state[Descriptors::THUMBNAIL] = descriptorsMap[Descriptors::THUMBNAIL];
} else {
state[Descriptors::THUMBNAIL] = "";
}
if (!descriptorsMap[Descriptors::MATURITY].isNull()) {
state[Descriptors::MATURITY] = descriptorsMap[Descriptors::MATURITY];
} else {
state[Descriptors::MATURITY] = "";
}
if (!descriptorsMap[Descriptors::CONTACT].isNull()) {
state[Descriptors::CONTACT] = descriptorsMap[Descriptors::CONTACT];
} else {
state[Descriptors::CONTACT] = "";
}
// copy array descriptors
state[Descriptors::IMAGES] = descriptorsMap[Descriptors::IMAGES].toList();
state[Descriptors::MANAGERS] = descriptorsMap[Descriptors::MANAGERS].toList();
state[Descriptors::TAGS] = descriptorsMap[Descriptors::TAGS].toList();
// parse capacity
@ -198,7 +230,7 @@ void DomainMetadata::maybeUpdateUsers() {
}
void DomainMetadata::sendDescriptors() {
QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)));
QString domainUpdateJSON = QString("{\"domain\":{\"meta\":%1}}").arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)));
const QUuid& domainID = DependencyManager::get<LimitedNodeList>()->getSessionUUID();
if (!domainID.isNull()) {
static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
@ -215,3 +247,22 @@ void DomainMetadata::sendDescriptors() {
#endif
}
}
bool DomainMetadata::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
QString domainMetadataJSON = QString("{\"domain\":{\"meta\":%1}, \"users\":%2}")
.arg(QString(QJsonDocument(get(DESCRIPTORS)).toJson(QJsonDocument::Compact)))
.arg(QString(QJsonDocument(get(USERS)).toJson(QJsonDocument::Compact)));
const QString URI_METADATA = "/metadata";
const QString EXPORTER_MIME_TYPE = "application/json";
if (url.path() == URI_METADATA) {
connection->respond(HTTPConnection::StatusCode200, domainMetadataJSON.toUtf8(), qPrintable(EXPORTER_MIME_TYPE));
return true;
}
#if DEV_BUILD || PR_BUILD
qCDebug(domain_metadata_exporter) << "Metadata request on URL " << url;
#endif
return false;
}

View file

@ -4,6 +4,7 @@
//
// Created by Zach Pomerantz on 5/25/2016.
// Copyright 2016 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -15,8 +16,9 @@
#include <QVariantMap>
#include <QJsonObject>
#include "HTTPManager.h"
class DomainMetadata : public QObject {
class DomainMetadata : public QObject, public HTTPRequestHandler {
Q_OBJECT
public:
@ -33,25 +35,29 @@ public:
static const QString DESCRIPTORS;
class Descriptors {
public:
static const QString NAME;
static const QString DESCRIPTION;
static const QString THUMBNAIL;
static const QString IMAGES;
static const QString CAPACITY;
static const QString RESTRICTION;
static const QString MATURITY;
static const QString HOSTS;
static const QString CONTACT;
static const QString MANAGERS;
static const QString TAGS;
};
DomainMetadata(QObject* domainServer);
DomainMetadata() = delete;
~DomainMetadata() = default;
// Get cached metadata
QJsonObject get();
QJsonObject get(const QString& group);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
public slots:
void descriptorsChanged();
void securityChanged(bool send);
void securityChanged() { securityChanged(true); }
void usersChanged();
protected:

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 9/26/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -70,7 +71,6 @@ const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace";
const int MIN_PORT = 1;
const int MAX_PORT = 65535;
int const DomainServer::EXIT_CODE_REBOOT = 234923;
QString DomainServer::_iceServerAddr { NetworkingConstants::ICE_SERVER_DEFAULT_HOSTNAME };
@ -119,7 +119,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
root.insert(requestSubobjectKey, subobject);
QJsonDocument doc { root };
QUrl url{ MetaverseAPI::getCurrentMetaverseServerURL().toString() + metaversePath };
QUrl url { MetaverseAPI::getCurrentMetaverseServerURL().toString() + metaversePath };
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
@ -267,6 +267,13 @@ DomainServer::DomainServer(int argc, char* argv[]) :
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
_metadata, &DomainMetadata::descriptorsChanged);
// update the metadata when a user (dis)connects
connect(this, &DomainServer::userConnected, _metadata, &DomainMetadata::usersChanged);
connect(this, &DomainServer::userDisconnected, _metadata, &DomainMetadata::usersChanged);
// update the metadata when security changes
connect(&_settingsManager, &DomainServerSettingsManager::updateNodePermissions, [this] { _metadata->securityChanged(true); });
qDebug() << "domain-server is running";
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
@ -328,11 +335,12 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_nodePingMonitorTimer->start(NODE_PING_MONITOR_INTERVAL_MSECS);
initializeExporter();
initializeMetadataExporter();
}
void DomainServer::parseCommandLine(int argc, char* argv[]) {
QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity Domain Server");
parser.setApplicationDescription("Vircadia Domain Server");
const QCommandLineOption versionOption = parser.addVersionOption();
const QCommandLineOption helpOption = parser.addHelpOption();
@ -421,6 +429,11 @@ DomainServer::~DomainServer() {
_contentManager->aboutToFinish();
_contentManager->terminate();
}
if (_httpMetadataExporterManager) {
_httpMetadataExporterManager->close();
delete _httpMetadataExporterManager;
}
if (_httpExporterManager) {
_httpExporterManager->close();
@ -765,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);
@ -1144,7 +1176,7 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
QUrl authorizationURL = _oauthProviderURL;
const QString OAUTH_AUTHORIZATION_PATH = "/oauth/authorize";
authorizationURL.setPath(OAUTH_AUTHORIZATION_PATH);
authorizationURL.setPath(MetaverseAPI::getCurrentMetaverseServerURLPath() + OAUTH_AUTHORIZATION_PATH);
QUrlQuery authorizationQuery;
@ -1422,7 +1454,7 @@ void DomainServer::sendPendingTransactionsToServer() {
transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback";
while (i != _pendingAssignmentCredits.end()) {
accountManager->sendRequest("api/v1/transactions",
accountManager->sendRequest("/api/v1/transactions",
AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation,
transactionCallbackParams, i.value()->postJson().toJson());
@ -1608,7 +1640,7 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate";
qCDebug(domain_server_ice) << "Updating ice-server address in High Fidelity Metaverse API to"
qCDebug(domain_server_ice) << "Updating ice-server address in Metaverse API to"
<< (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString());
static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address";
@ -1967,6 +1999,7 @@ const QString URI_OAUTH = "/oauth";
bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
const QString JSON_MIME_TYPE = "application/json";
const QString URI_ID = "/id";
const QString URI_ASSIGNMENT = "/assignment";
const QString URI_NODES = "/nodes";
const QString URI_SETTINGS = "/settings";
@ -2043,7 +2076,6 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
}
// check if this is a request for our domain ID
const QString URI_ID = "/id";
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
&& url.path() == URI_ID) {
QUuid domainID = nodeList->getSessionUUID();
@ -2550,7 +2582,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token";
QUrl tokenRequestUrl = _oauthProviderURL;
tokenRequestUrl.setPath(OAUTH_TOKEN_REQUEST_PATH);
tokenRequestUrl.setPath(MetaverseAPI::getCurrentMetaverseServerURLPath() + OAUTH_TOKEN_REQUEST_PATH);
const QString OAUTH_GRANT_TYPE_POST_STRING = "grant_type=authorization_code";
QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING;
@ -2864,7 +2896,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
// fire off a request to get this user's identity so we can see if we will let them in
QUrl profileURL = _oauthProviderURL;
profileURL.setPath("/api/v1/user/profile");
profileURL.setPath(MetaverseAPI::getCurrentMetaverseServerURLPath() + "/api/v1/user/profile");
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
qDebug() << "Sending profile request to: " << profileURL;
@ -3039,8 +3071,7 @@ void DomainServer::updateUpstreamNodes() {
updateReplicationNodes(Upstream);
}
void DomainServer::initializeExporter()
{
void DomainServer::initializeExporter() {
static const QString ENABLE_EXPORTER = "monitoring.enable_prometheus_exporter";
static const QString EXPORTER_PORT = "monitoring.prometheus_exporter_port";
@ -3056,7 +3087,39 @@ void DomainServer::initializeExporter()
if (isExporterEnabled && !_httpExporterManager) {
qCInfo(domain_server) << "Starting Prometheus exporter on port " << exporterPort;
_httpExporterManager = new HTTPManager(QHostAddress::Any, (quint16)exporterPort, QString("%1/resources/prometheus_exporter/").arg(QCoreApplication::applicationDirPath()), &_exporter);
_httpExporterManager = new HTTPManager
(
QHostAddress::Any,
(quint16)exporterPort,
QString("%1/resources/prometheus_exporter/").arg(QCoreApplication::applicationDirPath()),
&_exporter
);
}
}
void DomainServer::initializeMetadataExporter() {
static const QString ENABLE_EXPORTER = "metaverse.enable_metadata_exporter";
static const QString EXPORTER_PORT = "metaverse.metadata_exporter_port";
bool isMetadataExporterEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_EXPORTER).toBool();
int metadataExporterPort = _settingsManager.valueOrDefaultValueForKeyPath(EXPORTER_PORT).toInt();
if (metadataExporterPort < MIN_PORT || metadataExporterPort > MAX_PORT) {
qCWarning(domain_server) << "Metadata exporter port" << metadataExporterPort << "is out of range.";
isMetadataExporterEnabled = false;
}
qCDebug(domain_server) << "Setting up Metadata exporter.";
if (isMetadataExporterEnabled && !_httpMetadataExporterManager) {
qCInfo(domain_server) << "Starting Metadata exporter on port" << metadataExporterPort;
_httpMetadataExporterManager = new HTTPManager
(
QHostAddress::Any,
(quint16)metadataExporterPort,
QString("%1/resources/metadata_exporter/").arg(QCoreApplication::applicationDirPath()),
_metadata
);
}
}
@ -3682,7 +3745,7 @@ void DomainServer::screensharePresence(QString roomname, QUuid avatarID, int exp
callbackData.insert("roomname", roomname);
callbackData.insert("avatarID", avatarID.toString());
callbackParams.callbackData = callbackData;
const QString PATH = "api/v1/domains/%1/screenshare";
const QString PATH = "/api/v1/domains/%1/screenshare";
QString domain_id = uuidStringWithoutCurlyBraces(getID());
QJsonObject json, screenshare;
screenshare["username"] = verifiedUsername;

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 9/26/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -140,6 +141,7 @@ private slots:
void updateDownstreamNodes();
void updateUpstreamNodes();
void initializeExporter();
void initializeMetadataExporter();
void tokenGrantFinished();
void profileRequestFinished();
@ -240,6 +242,8 @@ private:
HTTPManager _httpManager;
HTTPManager* _httpExporterManager { nullptr };
HTTPManager* _httpMetadataExporterManager { nullptr };
std::unique_ptr<HTTPSManager> _httpsManager;
QHash<QUuid, SharedAssignmentPointer> _allAssignments;

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2014-06-24.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -1998,7 +1999,7 @@ void DomainServerSettingsManager::apiGetGroupID(const QString& groupName) {
callbackParams.jsonCallbackMethod = "apiGetGroupIDJSONCallback";
callbackParams.errorCallbackMethod = "apiGetGroupIDErrorCallback";
const QString GET_GROUP_ID_PATH = "api/v1/groups/names/%1";
const QString GET_GROUP_ID_PATH = "/api/v1/groups/names/%1";
DependencyManager::get<AccountManager>()->sendRequest(GET_GROUP_ID_PATH.arg(groupName),
AccountManagerAuth::Required,
QNetworkAccessManager::GetOperation, callbackParams);
@ -2064,7 +2065,7 @@ void DomainServerSettingsManager::apiGetGroupRanks(const QUuid& groupID) {
callbackParams.jsonCallbackMethod = "apiGetGroupRanksJSONCallback";
callbackParams.errorCallbackMethod = "apiGetGroupRanksErrorCallback";
const QString GET_GROUP_RANKS_PATH = "api/v1/groups/%1/ranks";
const QString GET_GROUP_RANKS_PATH = "/api/v1/groups/%1/ranks";
DependencyManager::get<AccountManager>()->sendRequest(GET_GROUP_RANKS_PATH.arg(groupID.toString().mid(1,36)),
AccountManagerAuth::Required,
QNetworkAccessManager::GetOperation, callbackParams);

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2014-06-24.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -108,7 +109,7 @@ public:
QStringList getDomainServerGroupNames();
QStringList getDomainServerBlacklistGroupNames();
// these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api
// these are used to locally cache the result of calling "/api/v1/groups/.../is_member/..." on metaverse's api
void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); }
void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID);
QUuid isGroupMember(const QString& name, const QUuid& groupID); // returns rank or -1 if not a member
@ -211,6 +212,8 @@ private:
/// guard read/write access from multiple threads to settings
QReadWriteLock _settingsLock { QReadWriteLock::Recursive };
friend class DomainServer;
};
#endif // hifi_DomainServerSettingsManager_h

View file

@ -10,7 +10,7 @@ import zipfile
print = functools.partial(print, flush=True)
ANDROID_PACKAGE_URL = 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/android/'
ANDROID_PACKAGE_URL = 'https://cdn-1.vircadia.com/eu-c-1/vircadia-public/dependencies/android/'
ANDROID_PACKAGES = {
'qt' : {
@ -68,7 +68,7 @@ ANDROID_PACKAGES = {
'includeLibs': ['libtbb.so', 'libtbbmalloc.so'],
},
'hifiAC': {
'baseUrl': 'https://content.vircadia.com/eu-c-1/vircadia-public/dependencies/',
'baseUrl': 'https://cdn-1.vircadia.com/eu-c-1/vircadia-public/dependencies/',
'file': 'codecSDK-android_armv8-2.0.zip',
'checksum': '1cbef929675818fc64c4101b72f84a6a'
},

View file

@ -211,8 +211,15 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
auto& networkAccessManager = NetworkAccessManager::getInstance();
QUrl publicKeyURL{ MetaverseAPI::getCurrentMetaverseServerURL() };
// qDebug() << "publicKeyURL" << publicKeyURL;
// qDebug() << "MetaverseAPI::getCurrentMetaverseServerURLPath()" << MetaverseAPI::getCurrentMetaverseServerURLPath();
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
publicKeyURL.setPath(publicKeyPath);
publicKeyURL.setPath("/" + MetaverseAPI::getCurrentMetaverseServerURLPath() + publicKeyPath);
// qDebug() << "publicKeyPath" << publicKeyPath;
// qDebug() << "publicKeyURL.setPath" << "/" + MetaverseAPI::getCurrentMetaverseServerURLPath() + publicKeyPath;
// qDebug() << "publicKeyURL" << publicKeyURL;
// qDebug() << "publicKeyURL.isValid()" << publicKeyURL.isValid();
// qDebug() << "publicKeyURL.errorString()" << publicKeyURL.errorString();
QNetworkRequest publicKeyRequest { publicKeyURL };
publicKeyRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);

View file

@ -238,6 +238,8 @@ target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries
target_openssl()
target_bullet()
set(OpenGL_GL_PREFERENCE "LEGACY")
target_opengl()
add_crashpad()
target_breakpad()

View file

@ -1,861 +1,71 @@
{
"Anime boy": {
"attachments": [
],
"avatarEntites": [
{
"properties": {
"acceleration": {
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 6.915350914001465,
"ageAsText": "0 hours 0 minutes 6 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"x": -0.10961885005235672,
"y": -0.19444090127944946,
"z": -0.15760529041290283
},
"center": {
"x": 2.6226043701171875e-06,
"y": -0.13999652862548828,
"z": -0.04999971389770508
},
"dimensions": {
"x": 0.21924294531345367,
"y": 0.10888873785734177,
"z": 0.2152111530303955
},
"tfl": {
"x": 0.10962409526109695,
"y": -0.0855521634221077,
"z": 0.057605862617492676
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-06-06T17:27:53Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"x": 0.21924294531345367,
"y": 0.07768379896879196,
"z": 0.2055898904800415
},
"dynamic": false,
"editionNumber": 15,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{5d20c775-a0d7-4163-b158-4e0a784a4625}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
"itemDescription": "Wear these, and others will respect your authoritah.",
"itemLicense": "",
"itemName": "Aviators",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1528306178314655,
"lastEditedBy": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"x": 2.6226043701171875e-06,
"y": -0.13999652862548828,
"z": -0.04999971389770508
},
"localRotation": {
"w": 0.9969173073768616,
"x": -0.07845909893512726,
"y": 0,
"z": 0
},
"locked": false,
"marketplaceID": "40d879ec-93f0-4b4a-8c58-dd6349bdb058",
"modelURL": "http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx",
"name": "",
"naturalDimensions": {
"x": 0.1660931408405304,
"y": 0.05885136127471924,
"z": 0.15574991703033447
},
"naturalPosition": {
"x": 0,
"y": 1.6633577346801758,
"z": 0.048884183168411255
},
"originalTextures": "{\n \"aviator:Eyewear2F\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Diffuse.png\",\n \"aviator:Eyewear2F1\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Specular.png\"\n}\n",
"owningAvatarID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
"parentID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}",
"parentJointIndex": 66,
"position": {
"x": 2.6226043701171875e-06,
"y": -0.13999652862548828,
"z": -0.04999971389770508
},
"queryAACube": {
"scale": 0.9313028454780579,
"x": -1.4091639518737793,
"y": -10.133878707885742,
"z": 1.9983724355697632
},
"registrationPoint": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 2,
"texturesSize": 1310720,
"verticesCount": 982
},
"restitution": 0.5,
"rotation": {
"w": 0.9969173073768616,
"x": -0.07845909893512726,
"y": 0,
"z": 0
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
}
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst",
"version": 3
},
"Anime girl": {
"attachments": [
],
"avatarEntites": [
{
"properties": {
"acceleration": {
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 19.66267967224121,
"ageAsText": "0 hours 0 minutes 19 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"x": -0.10536206513643265,
"y": -0.16647332906723022,
"z": -0.12632352113723755
},
"center": {
"x": 0,
"y": -0.12999999523162842,
"z": -0.030000001192092896
},
"dimensions": {
"x": 0.2107241302728653,
"y": 0.07294666767120361,
"z": 0.1926470398902893
},
"tfl": {
"x": 0.10536206513643265,
"y": -0.09352666139602661,
"z": 0.06632351875305176
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-06-05T00:10:37Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"x": 0.2107241302728653,
"y": 0.07294666767120361,
"z": 0.1926470398902893
},
"dynamic": false,
"editionNumber": 5,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{1586b83a-2af7-4532-9bfb-82fe3f5d5ce9}",
"ignoreForCollisions": false,
"itemArtist": "moam_00",
"itemCategories": "Wearables",
"itemDescription": "Perfect for side-glancin'.",
"itemLicense": "",
"itemName": "Blacker Fem Glasses",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1528157470041658,
"lastEditedBy": "{425df1a8-289b-42fc-819c-c3b2a12d7165}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"x": 0,
"y": -0.12999999523162842,
"z": -0.029999999329447746
},
"localRotation": {
"w": 1,
"x": -2.2351741790771484e-08,
"y": 3.4924596548080444e-10,
"z": 3.725290298461914e-09
},
"locked": false,
"marketplaceID": "06781d12-9139-48f4-ac2a-417dde090981",
"modelURL": "http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx",
"name": "Female Glasses 3 by Mario Andrade",
"naturalDimensions": {
"x": 0.16209548711776733,
"y": 0.05611282214522362,
"z": 0.14819003641605377
},
"naturalPosition": {
"x": 0,
"y": -7.636845111846924e-08,
"z": 0
},
"originalTextures": "{\n \"file49\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Mixed_AO.jpg\",\n \"file81\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Metallic.jpg\",\n \"file84\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Roughness.jpg\",\n \"file86\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Base_Color.jpg\",\n \"file87\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Normal_DirectX.jpg\"\n}\n",
"owningAvatarID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
"parentID": "{1277f725-fbb4-478b-ae79-1241fd90e508}",
"parentJointIndex": 66,
"position": {
"x": 0,
"y": -0.12999999523162842,
"z": -0.029999999329447746
},
"queryAACube": {
"scale": 0.8840523958206177,
"x": -2.6587564945220947,
"y": -10.162277221679688,
"z": -0.9548344016075134
},
"registrationPoint": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 5,
"texturesSize": 0,
"verticesCount": 1156
},
"restitution": 0.5,
"rotation": {
"w": 1,
"x": -2.2351741790771484e-08,
"y": 3.4924596548080444e-10,
"z": 3.725290298461914e-09
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
}
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst",
"version": 3
},
"Last Legends: Male": {
"attachments": [
],
"avatarEntites": [
{
"properties": {
"acceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 321.8835144042969,
"ageAsText": "0 hours 5 minutes 21 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"blue": -0.03950843587517738,
"green": 0.20785385370254517,
"red": -0.04381325840950012,
"x": -0.04381325840950012,
"y": 0.20785385370254517,
"z": -0.03950843587517738
},
"center": {
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"dimensions": {
"blue": 0.07901687175035477,
"green": 0.044292300939559937,
"red": 0.08762651681900024,
"x": 0.08762651681900024,
"y": 0.044292300939559937,
"z": 0.07901687175035477
},
"tfl": {
"blue": 0.03950843587517738,
"green": 0.2521461546421051,
"red": 0.04381325840950012,
"x": 0.04381325840950012,
"y": 0.2521461546421051,
"z": 0.03950843587517738
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-07-26T23:56:46Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"blue": 0.07229919731616974,
"green": 0.06644226610660553,
"red": 0.03022606298327446,
"x": 0.03022606298327446,
"y": 0.06644226610660553,
"z": 0.07229919731616974
},
"dynamic": false,
"editionNumber": 58,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{03053239-bb37-4c51-a013-a1772baaeed5}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
"itemDescription": "A cool scifi watch for your avatar!",
"itemLicense": "",
"itemName": "Scifi Watch",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1532649569894305,
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"localRotation": {
"w": 0.5910986065864563,
"x": -0.48726415634155273,
"y": -0.4088630974292755,
"z": 0.49599072337150574
},
"locked": false,
"marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90",
"modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx",
"name": "Scifi Watch by Jimi",
"naturalDimensions": {
"blue": 0.055614765733480453,
"green": 0.0511094331741333,
"red": 0.023250818252563477,
"x": 0.023250818252563477,
"y": 0.0511094331741333,
"z": 0.055614765733480453
},
"naturalPosition": {
"blue": -0.06031447649002075,
"green": 1.4500460624694824,
"red": 0.6493338942527771,
"x": 0.6493338942527771,
"y": 1.4500460624694824,
"z": -0.06031447649002075
},
"originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n",
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentJointIndex": 16,
"position": {
"blue": 0,
"green": 0.23000000417232513,
"red": 0,
"x": 0,
"y": 0.23000000417232513,
"z": 0
},
"queryAACube": {
"scale": 0.3082179129123688,
"x": 495.7716979980469,
"y": 498.345703125,
"z": 498.52044677734375
},
"registrationPoint": {
"blue": 0.5,
"green": 0.5,
"red": 0.5,
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 5,
"texturesSize": 786432,
"verticesCount": 273
},
"restitution": 0.5,
"rotation": {
"w": 0.5910986065864563,
"x": -0.48726415634155273,
"y": -0.4088630974292755,
"z": 0.49599072337150574
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
},
{
"properties": {
"acceleration": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"actionData": "",
"age": 308.8044128417969,
"ageAsText": "0 hours 5 minutes 8 seconds",
"angularDamping": 0.39346998929977417,
"angularVelocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"animation": {
"allowTranslation": true,
"currentFrame": 0,
"firstFrame": 0,
"fps": 30,
"hold": false,
"lastFrame": 100000,
"loop": true,
"running": false,
"url": ""
},
"boundingBox": {
"brn": {
"blue": -0.2340194433927536,
"green": -0.07067721337080002,
"red": -0.17002610862255096,
"x": -0.17002610862255096,
"y": -0.07067721337080002,
"z": -0.2340194433927536
},
"center": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"dimensions": {
"blue": 0.3883880078792572,
"green": 0.18139348924160004,
"red": 0.34038791060447693,
"x": 0.34038791060447693,
"y": 0.18139348924160004,
"z": 0.3883880078792572
},
"tfl": {
"blue": 0.1543685644865036,
"green": 0.11071627587080002,
"red": 0.17036180198192596,
"x": 0.17036180198192596,
"y": 0.11071627587080002,
"z": 0.1543685644865036
}
},
"canCastShadow": true,
"certificateID": "",
"clientOnly": true,
"cloneAvatarEntity": false,
"cloneDynamic": false,
"cloneLifetime": 300,
"cloneLimit": 0,
"cloneOriginID": "{00000000-0000-0000-0000-000000000000}",
"cloneable": false,
"collidesWith": "",
"collisionMask": 0,
"collisionSoundURL": "",
"collisionless": false,
"collisionsWillMove": false,
"compoundShapeURL": "",
"created": "2018-07-26T23:56:46Z",
"damping": 0.39346998929977417,
"density": 1000,
"description": "",
"dimensions": {
"blue": 0.38838762044906616,
"green": 0.16981728374958038,
"red": 0.33466479182243347,
"x": 0.33466479182243347,
"y": 0.16981728374958038,
"z": 0.38838762044906616
},
"dynamic": false,
"editionNumber": 18,
"entityInstanceNumber": 0,
"friction": 0.5,
"gravity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"href": "",
"id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}",
"ignoreForCollisions": false,
"itemArtist": "jyoum",
"itemCategories": "Wearables",
"itemDescription": "A stylish and classic piece of headwear for your avatar.",
"itemLicense": "",
"itemName": "Fedora",
"jointRotations": [
],
"jointRotationsSet": [
],
"jointTranslations": [
],
"jointTranslationsSet": [
],
"lastEdited": 1532649698129709,
"lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"lifetime": -1,
"limitedRun": 4294967295,
"localPosition": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"localRotation": {
"w": 0.9998477101325989,
"x": -9.898545982878204e-09,
"y": 5.670873406415922e-07,
"z": 0.017452405765652657
},
"locked": false,
"marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc",
"modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx",
"name": "",
"naturalDimensions": {
"blue": 0.320981502532959,
"green": 0.14034485816955566,
"red": 0.2765824794769287,
"x": 0.2765824794769287,
"y": 0.14034485816955566,
"z": 0.320981502532959
},
"naturalPosition": {
"blue": 0.022502630949020386,
"green": 1.7460365295410156,
"red": 0.000143393874168396,
"x": 0.000143393874168396,
"y": 1.7460365295410156,
"z": 0.022502630949020386
},
"originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n",
"owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}",
"parentJointIndex": 66,
"position": {
"blue": -0.039825439453125,
"green": 0.02001953125,
"red": 0.0001678466796875,
"x": 0.0001678466796875,
"y": 0.02001953125,
"z": -0.039825439453125
},
"queryAACube": {
"scale": 1.6202316284179688,
"x": 495.21051025390625,
"y": 498.5577697753906,
"z": 497.6370849609375
},
"registrationPoint": {
"blue": 0.5,
"green": 0.5,
"red": 0.5,
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"relayParentJoints": false,
"renderInfo": {
"drawCalls": 1,
"hasTransparent": false,
"texturesCount": 2,
"texturesSize": 327680,
"verticesCount": 719
},
"restitution": 0.5,
"rotation": {
"w": 0.9998477101325989,
"x": -9.898545982878204e-09,
"y": 5.670873406415922e-07,
"z": 0.017452405765652657
},
"script": "",
"scriptTimestamp": 0,
"serverScripts": "",
"shapeType": "box",
"staticCertificateVersion": 0,
"textures": "",
"type": "Model",
"userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}",
"velocity": {
"blue": 0,
"green": 0,
"red": 0,
"x": 0,
"y": 0,
"z": 0
},
"visible": true
}
}
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst",
"version": 3
},
"Last legends Female": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/8d823be5-6197-4418-b984-eb94160ed956-v1/LLFemale_Clothes.fst",
"version": 3
},
"Matthew": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst",
"version": 3
},
"Priscilla": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst",
"version": 3
},
"Woody": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "http://mpassets.highfidelity.com/ad348528-de38-420c-82bb-054cb22163f5-v1/mannequin.fst",
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Woody/mannequin.fst",
"version": 3
},
"Kim": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Kim/fbx/Kim.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Kim/img/icon.png",
"version": 3
},
"Mason": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Mason/fbx/Mason.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Mason/img/icon.png",
"version": 3
},
"Mike": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Mike/fbx/Mike.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Mike/img/icon.png",
"version": 3
},
"Sean": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Sean/fbx/Sean.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Sean/img/icon.png",
"version": 3
},
"Summer": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Summer/fbx/Summer.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Summer/img/icon.png",
"version": 3
},
"Tanya": {
"attachments": [
],
"avatarEntites": [
],
"avatarScale": 1,
"avatarUrl": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Tanya/fbx/Tanya.fst",
"avatarIcon": "https://cdn-1.vircadia.com/us-e-1/Bazaar/Avatars/Tanya/img/icon.png",
"version": 3
}
}
}

View file

@ -3,9 +3,9 @@
"channels": [
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
@ -72,46 +72,20 @@
{ "from": { "makeAxis" : [
["Keyboard.Left"],
["Keyboard.Right"]
]
]
},
"when": ["Application.CameraFirstPerson", "!Keyboard.Shift"],
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.Left"],
["Keyboard.Right"]
]
},
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"],
"to": "Actions.Yaw"
{ "from": "Keyboard.Left",
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
"to": "Actions.LATERAL_LEFT"
},
{ "from": { "makeAxis" : [
["Keyboard.Left"],
["Keyboard.Right"]
]
},
"when": ["Application.CameraThirdPerson", "!Keyboard.Shift"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.Left"],
["Keyboard.Right"]
]
},
"when": ["Application.CameraLookAt", "!Keyboard.Shift"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.Left"],
["Keyboard.Right"]
]
},
"when": ["Application.CameraSelfie", "!Keyboard.Shift"],
"to": "Actions.Yaw"
{ "from": "Keyboard.Right",
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"],
"to": "Actions.LATERAL_RIGHT"
},
{ "from": { "makeAxis" : [
@ -119,53 +93,18 @@
["Keyboard.D"]
]
},
"when": ["Application.CameraFirstPerson", "!Keyboard.Control"],
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"],
"to": "Actions.Yaw"
{ "from": "Keyboard.A",
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
"to": "Actions.LATERAL_LEFT"
},
{ "from": { "makeAxis" : [
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraThirdPerson", "!Keyboard.Control"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraLookAt", "!Keyboard.Control"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraSelfie", "!Keyboard.Control"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.TouchpadLeft"],
["Keyboard.TouchpadRight"]
]
},
"when": "Application.CameraFirstPerson",
"to": "Actions.Yaw"
{ "from": "Keyboard.D",
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"],
"to": "Actions.LATERAL_RIGHT"
},
{ "from": { "makeAxis" : [
@ -173,39 +112,12 @@
["Keyboard.TouchpadRight"]
]
},
"when": "Application.CameraFirstPersonLookat",
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.TouchpadLeft"],
["Keyboard.TouchpadRight"]
]
},
"when": "Application.CameraThirdPerson",
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.TouchpadLeft"],
["Keyboard.TouchpadRight"]
]
},
"when": "Application.CameraLookAt",
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.TouchpadLeft"],
["Keyboard.TouchpadRight"]
]
},
"when": "Application.CameraSelfie",
"when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": "Keyboard.RightMouseButton",
"when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"],
"to": "Actions.DeltaYaw",
"filters":
[
@ -213,8 +125,17 @@
]
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"to": "Actions.DeltaYaw",
"when": "Application.CaptureMouse",
"filters":
[
{ "type": "scale", "scale": 0.2 }
]
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
"when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "Keyboard.RightMouseButton"],
"when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"],
"to": "Actions.DeltaPitch",
"filters":
[
@ -222,6 +143,15 @@
]
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
"to": "Actions.DeltaPitch",
"when": "Application.CaptureMouse",
"filters":
[
{ "type": "scale", "scale": 0.2 }
]
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
"when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"],
"to": "Actions.DeltaPitch",

Binary file not shown.

View file

@ -386,6 +386,10 @@ Item {
visible: root.expanded
text: "LOD: " + root.lodStatus;
}
StatText {
visible: root.expanded
text: "Entity Updates: " + root.numEntityUpdates + " / " + root.numNeededEntityUpdates;
}
}
}
}

View file

@ -8,7 +8,6 @@ WebView {
id: webview
url: "https://vircadia.com/"
profile: FileTypeProfile;
property var parentRoot: null
// Create a global EventBridge object for raiseAndLowerKeyboard.

View file

@ -49,6 +49,12 @@ ScrollingWindow {
desktop.setAutoAdd(auto);
}
function openExternalBrowser() {
Qt.openUrlExternally(addressBar.text);
root.shown = false;
root.windowClosed();
}
Item {
id:item
width: pane.contentWidth
@ -58,34 +64,49 @@ ScrollingWindow {
id: buttons
spacing: 4
anchors.top: parent.top
anchors.topMargin: 8
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: 8
HiFiGlyphs {
id: back;
enabled: webview.canGoBack;
id: back
enabled: webview.canGoBack
text: hifi.glyphs.backward
color: enabled ? hifi.colors.text : hifi.colors.disabledText
color: enabled ? (backMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goBack() }
MouseArea {
id: backMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: webview.goBack();
}
}
HiFiGlyphs {
id: forward;
enabled: webview.canGoForward;
id: forward
enabled: webview.canGoForward
text: hifi.glyphs.forward
color: enabled ? hifi.colors.text : hifi.colors.disabledText
color: enabled ? (forwardMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
MouseArea {
id: forwardMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: webview.goForward();
}
}
HiFiGlyphs {
id: reload;
enabled: webview.canGoForward;
id: reload
enabled: url !== ""
text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload
color: enabled ? hifi.colors.text : hifi.colors.disabledText
color: enabled ? (reloadMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
MouseArea {
id: reloadMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: webview.loading ? webview.stop() : webview.reload();
}
}
}
@ -94,52 +115,82 @@ ScrollingWindow {
id: border
height: 48
anchors.top: parent.top
anchors.topMargin: 8
anchors.topMargin: 4
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: buttons.right
anchors.leftMargin: 8
HiFiGlyphs {
id: externalBrowser
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 4
enabled: !HMD.active && url !== ""
font.family: "FontAwesome"
text: "\uf24d"
rotation: -90
color: enabled ? (externalBrowserMouseArea.containsMouse ? hifi.colors.blueHighlight : hifi.colors.faintGray) : hifi.colors.lightGray
size: 32
MouseArea {
id: externalBrowserMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: openExternalBrowser();
}
}
Item {
id: barIcon
width: parent.height
height: parent.height
Image {
source: webview.icon;
source: webview.loading ? "" : webview.icon
x: (parent.height - height) / 2
y: (parent.width - width) / 2
sourceSize: Qt.size(width, height);
verticalAlignment: Image.AlignVCenter;
width: 28
height: 28
verticalAlignment: Image.AlignVCenter
horizontalAlignment: Image.AlignHCenter
}
}
TextField {
id: addressBar
anchors.right: parent.right
anchors.rightMargin: 8
anchors.right: externalBrowser.left
anchors.rightMargin: 32
anchors.left: barIcon.right
anchors.leftMargin: 0
anchors.verticalCenter: parent.verticalCenter
focus: true
colorScheme: hifi.colorSchemes.dark
placeholderText: "Enter URL"
inputMethodHints: Qt.ImhUrlCharactersOnly
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
if (text.indexOf("http") != 0) {
if (text.indexOf("http") !== 0) {
text = "http://" + text;
}
// Setting webiew.url directly doesn't add the URL to the navigation history.
//webview.url = text;
// The following works around this bug.
text = encodeURI(text);
text = text.replace(/;/g, "%3b"); // Prevent script injection.
text = text.replace(/'/g, "%25"); // ""
webview.runJavaScript("window.location='" + text + "'");
root.hidePermissionsBar();
root.keyboardRaised = false;
webview.url = text;
break;
}
}
}
}
Rectangle {
@ -204,7 +255,7 @@ ScrollingWindow {
parentRoot: root
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.topMargin: 4
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
@ -216,7 +267,7 @@ ScrollingWindow {
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
if (event.modifiers === Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
@ -224,4 +275,5 @@ ScrollingWindow {
break;
}
}
} // dialog

View file

@ -437,6 +437,10 @@ Item {
visible: root.expanded
text: "LOD: " + root.lodStatus;
}
StatText {
visible: root.expanded
text: "Entity Updates: " + root.numEntityUpdates + " / " + root.numNeededEntityUpdates;
}
}
}
}

View file

@ -3,6 +3,7 @@
//
// Created by David Rowe on 16 Dec 2016.
// Copyright 2016 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -17,22 +18,31 @@ Item {
anchors.fill: parent
property string url: ""
property string scriptUrl: null
property bool useBackground: true
onUrlChanged: {
load(root.url, root.scriptUrl);
load(root.url, root.scriptUrl, root.useBackground);
}
onScriptUrlChanged: {
if (root.item) {
root.item.scriptUrl = root.scriptUrl;
} else {
load(root.url, root.scriptUrl);
load(root.url, root.scriptUrl, root.useBackground);
}
}
onUseBackgroundChanged: {
if (root.item) {
root.item.useBackground = root.useBackground;
} else {
load(root.url, root.scriptUrl, root.useBackground);
}
}
property var item: null
function load(url, scriptUrl) {
function load(url, scriptUrl, useBackground) {
// Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375
if (root.item != null) {
root.item.url = "about:blank"
@ -43,11 +53,12 @@ Item {
root.item = newItem
root.item.url = url
root.item.scriptUrl = scriptUrl
root.item.useBackground = useBackground
})
}
Component.onCompleted: {
load(root.url, root.scriptUrl);
load(root.url, root.scriptUrl, root.useBackground);
}
signal sendToScript(var message);

View file

@ -16,6 +16,7 @@ Item {
property alias webViewCoreProfile: webViewCore.profile
property string webViewCoreUserAgent
property bool useBackground: webViewCore.useBackground
property string userScriptUrl: ""
property string urlTag: "noDownload=false";
@ -98,6 +99,7 @@ Item {
width: parent.width
height: parent.height
backgroundColor: (flick.useBackground) ? "white" : "transparent"
profile: HFWebEngineProfile;
settings.pluginsEnabled: true

View file

@ -14,6 +14,7 @@ Item {
property alias webViewCoreProfile: webViewCore.profile
property string webViewCoreUserAgent
property bool useBackground: webViewCore.useBackground
property string userScriptUrl: ""
property string urlTag: "noDownload=false";

View file

@ -23,6 +23,7 @@ Item {
property bool passwordField: false
property alias flickable: webroot.interactive
property alias blurOnCtrlShift: webroot.blurOnCtrlShift
property alias useBackground: webroot.useBackground
function stop() {
webroot.stop();

View file

@ -148,7 +148,7 @@ Windows.ScrollingWindow {
}
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i, /\.glb\b/i];
if (selectedItemCount > 1) {
return false;

View file

@ -37,17 +37,6 @@ ListModel {
return trimmedUrl;
}
function imageExists(imageUrl) {
var http = new XMLHttpRequest();
http.open('HEAD', imageUrl, false);
http.send();
return http.status !== 404;
}
function makeThumbnailUrl(avatarUrl) {
var marketId = extractMarketId(avatarUrl);
if (marketId !== '') {
@ -55,17 +44,18 @@ ListModel {
}
var avatarThumbnailFileUrl = trimFileExtension(avatarUrl) + ".jpg";
var thumbnailExist = imageExists(avatarThumbnailFileUrl);
if (!thumbnailExist) {
return '';
}
return avatarThumbnailFileUrl;
}
function makeAvatarObject(avatar, avatarName) {
var avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
var avatarThumbnailUrl;
if (!avatar.avatarIcon) {
avatarThumbnailUrl = makeThumbnailUrl(avatar.avatarUrl);
} else {
avatarThumbnailUrl = avatar.avatarIcon;
}
return {
'name' : avatarName,

View file

@ -456,7 +456,7 @@ Rectangle {
id: avatarCollisionSoundUrlInputText
font.pixelSize: 17
Layout.fillWidth: true
placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-'
placeholderText: "https://cdn-1.vircadia.com/eu-c-1/vircadia-public/sounds/Collisions-"
onFocusChanged: {
keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus);

View file

@ -3,6 +3,7 @@
//
// Created by David Rowe on 18 Apr 2017
// Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -33,12 +34,12 @@ Rectangle {
anchors.left: parent.left
anchors.leftMargin: 70
RalewayRegular {
text: "Build " + HiFiAbout.buildVersion
text: "Build " + About.buildVersion
size: 16
color: "white"
}
RalewayRegular {
text: "Released " + HiFiAbout.buildDate
text: "Released " + About.buildDate
size: 16
color: "white"
}
@ -56,7 +57,7 @@ Rectangle {
text: "<a href=\"https://github.com/kasenvr/project-athena\">Vircadia Github</a>."
size: 20
onLinkActivated: {
HiFiAbout.openUrl("https:/github.com/kasenvr/project-athena");
About.openUrl("https:/github.com/kasenvr/project-athena");
}
}
@ -70,13 +71,13 @@ Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
HiFiAbout.openUrl("https://www.qt.io/");
About.openUrl("https://www.qt.io/");
}
}
}
RalewayRegular {
color: "white"
text: "Built using Qt " + HiFiAbout.qtVersion
text: "Built using Qt " + About.qtVersion
size: 12
anchors.verticalCenter: parent.verticalCenter
}
@ -102,7 +103,7 @@ Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
HiFiAbout.openUrl("http://opus-codec.org/");
About.openUrl("http://opus-codec.org/");
}
}
}
@ -131,7 +132,7 @@ Rectangle {
text: "Distributed under the <a href=\"http://www.apache.org/licenses/LICENSE-2.0.html\">Apache License, Version 2.0.</a>."
size: 14
onLinkActivated: {
HiFiAbout.openUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
About.openUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
}
}
}

View file

@ -148,7 +148,7 @@ Rectangle {
}
function canAddToWorld(path) {
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i, /\.glb\b/i];
if (selectedItemCount > 1) {
return false;

View file

@ -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;

View file

@ -25,7 +25,7 @@ XmlListModel {
readonly property string realPrefix: prefix.match('.*/$') ? prefix : (prefix + "/")
readonly property string nameRegex: realPrefix + (filter ? (".*" + filter) : "") + ".*\." + extension
readonly property string nameQuery: "Key/substring-before(substring-after(string(), '" + prefix + "'), '." + extension + "')"
readonly property string baseUrl: "http://s3.amazonaws.com/hifi-public"
readonly property string baseUrl: "https://cdn-1.vircadia.com/eu-c-1/vircadia-public"
// FIXME need to urlencode prefix?
source: baseUrl + "?prefix=" + realPrefix

View file

@ -27,6 +27,7 @@ Item {
width: parent.width
property string title: "Controls"
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve"]
HifiConstants { id: hifi }
@ -244,7 +245,7 @@ Item {
source: InputConfiguration.configurationLayout(box.textAt(box.currentIndex));
onLoaded: {
if (loader.item.hasOwnProperty("pluginName")) {
if (box.textAt(box.currentIndex) === "HTC Vive") {
if (openVRDevices.indexOf(box.textAt(box.currentIndex)) !== -1) {
loader.item.pluginName = "OpenVR";
} else {
loader.item.pluginName = box.textAt(box.currentIndex);
@ -298,7 +299,7 @@ Item {
loader.source = "";
var selectedDevice = box.textAt(box.currentIndex);
var source = "";
if (selectedDevice == "HTC Vive") {
if (openVRDevices.indexOf(selectedDevice) !== -1) {
source = InputConfiguration.configurationLayout("OpenVR");
} else {
source = InputConfiguration.configurationLayout(selectedDevice);

View file

@ -894,7 +894,7 @@ Flickable {
hoverEnabled: true
onEntered: privacyPolicyUnderline.visible = true;
onExited: privacyPolicyUnderline.visible = false;
onClicked: HiFiAbout.openUrl("https://vircadia.com/privacy-policy");
onClicked: About.openUrl("https://vircadia.com/privacy-policy");
}
}

View file

@ -296,7 +296,7 @@
"lastEdited": 1537901430334966,
"lastEditedBy": "{69540019-db48-4375-86c8-ac1a4a90d043}",
"locked": true,
"modelURL": "http://hifi-content.s3.amazonaws.com/alexia/LoadingScreens/floor.fbx",
"modelURL": "https://cdn-1.vircadia.com/eu-c-1/vircadia-content/alexia/LoadingScreens/floor.fbx",
"name": "floorModel",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
"position": {

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,16 @@ QString AboutUtil::getQtVersion() const {
}
void AboutUtil::openUrl(const QString& url) const {
auto abboutUtilInstance = AboutUtil::getInstance();
if (!abboutUtilInstance) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(abboutUtilInstance, "openUrl", Q_ARG(const QString&, url));
return;
}
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto offscreenUi = DependencyManager::get<OffscreenUi>();

View file

@ -4,6 +4,7 @@
//
// Created by Vlad Stelmahovsky on 15/5/2018.
// Copyright 2018 High Fidelity, Inc.
// Copyright 2020 Vircadia Contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -16,6 +17,29 @@
#include <QObject>
/**jsdoc
* The <code>About</code> API provides information about the version of Interface that is currently running. It also has the
* functionality to open a web page in an Interface browser window.
*
* @namespace About
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @property {string} platform - The name of the Interface platform running, e,g., <code>"Vircadia"</code> for the Vircadia.
* <em>Read-only.</em>
* @property {string} buildDate - The build date of Interface that is currently running. <em>Read-only.</em>
* @property {string} buildVersion - The build version of Interface that is currently running. <em>Read-only.</em>
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
*
* @example <caption>Report information on the version of Interface currently running.</caption>
* print("Interface platform: " + About.platform);
* print("Interface build date: " + About.buildDate);
* print("Interface version: " + About.buildVersion);
* print("Qt version: " + About.qtVersion);
*/
/**jsdoc
* The <code>HifiAbout</code> API provides information about the version of Interface that is currently running. It also
* has the functionality to open a web page in an Interface browser window.
*
@ -25,19 +49,21 @@
* @hifi-client-entity
* @hifi-avatar
*
* @deprecated This API is deprecated and will be removed. Use the {@link About} API instead.
*
* @property {string} platform - The name of the Interface platform running, e,g., <code>"Vircadia"</code> for the Vircadia.
* <em>Read-only.</em>
* @property {string} buildDate - The build date of Interface that is currently running. <em>Read-only.</em>
* @property {string} buildVersion - The build version of Interface that is currently running. <em>Read-only.</em>
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
*
* @example <caption>Report build information for the version of Interface currently running.</caption>
* print("HiFi build date: " + HifiAbout.buildDate); // Returns the build date of the version of Interface currently running on your machine.
* print("HiFi version: " + HifiAbout.buildVersion); // Returns the build version of Interface currently running on your machine.
* print("Qt version: " + HifiAbout.qtVersion); // Returns the Qt version details of the version of Interface currently running on your machine.
* @borrows About.openUrl as openUrl
*/
class AboutUtil : public QObject {
Q_OBJECT
Q_PROPERTY(QString platform READ getPlatformName CONSTANT)
Q_PROPERTY(QString buildDate READ getBuildDate CONSTANT)
Q_PROPERTY(QString buildVersion READ getBuildVersion CONSTANT)
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
@ -45,6 +71,7 @@ public:
static AboutUtil* getInstance();
~AboutUtil() {}
QString getPlatformName() const { return "Vircadia"; }
QString getBuildDate() const;
QString getBuildVersion() const;
QString getQtVersion() const;
@ -52,8 +79,8 @@ public:
public slots:
/**jsdoc
* Display a web page in an Interface browser window.
* @function HifiAbout.openUrl
* Display a web page in an Interface browser window or the tablet.
* @function About.openUrl
* @param {string} url - The URL of the web page you want to view in Interface.
*/
void openUrl(const QString &url) const;

View file

@ -253,6 +253,7 @@
#include <DesktopPreviewProvider.h>
#include "AboutUtil.h"
#include "ExternalResource.h"
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -680,6 +681,9 @@ private:
* <tr><td><code>CameraIndependent</code></td><td>number</td><td>number</td><td>The camera is in independent mode.</td></tr>
* <tr><td><code>CameraEntity</code></td><td>number</td><td>number</td><td>The camera is in entity mode.</td></tr>
* <tr><td><code>InHMD</code></td><td>number</td><td>number</td><td>The user is in HMD mode.</td></tr>
* <tr><td><code>CaptureMouse</code></td><td>number</td><td>number</td><td>The mouse is captured. In this mode,
* the mouse is invisible and cannot leave the bounds of Interface, as long as Interface is the active window and
* no menu item is selected.</td></tr>
* <tr><td><code>AdvancedMovement</code></td><td>number</td><td>number</td><td>Advanced movement (walking) controls are
* enabled.</td></tr>
* <tr><td><code>StrafeEnabled</code></td><td>number</td><td>number</td><td>Strafing is enabled</td></tr>
@ -715,6 +719,7 @@ static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid";
static const QString STATE_LEFT_HAND_DOMINANT = "LeftHandDominant";
static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant";
static const QString STATE_STRAFE_ENABLED = "StrafeEnabled";
static const QString STATE_CAPTURE_MOUSE = "CaptureMouse";
// Statically provided display and input plugins
extern DisplayPluginList getDisplayPlugins();
@ -918,7 +923,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<MessagesClient>();
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON,
STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE,
STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, STATE_CAPTURE_MOUSE,
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED,
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
DependencyManager::set<UserInputMapper>();
@ -1352,10 +1357,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(),
&DialogsManager::showDomainLoginDialog);
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this,
&Application::updateWindowTitle);
connect(domainAccountManager.data(), &DomainAccountManager::authRequired,
dialogsManager.data(), &DialogsManager::showDomainLoginDialog);
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, this, &Application::updateWindowTitle);
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &Application::updateWindowTitle);
// ####### TODO: Connect any other signals from domainAccountManager.
// use our MyAvatar position and quat for address manager path
@ -1891,6 +1896,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAPTURE_MOUSE, []() -> float {
return qApp->getCamera().getCaptureMouse() ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
});
@ -2406,6 +2414,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateSystemTabletMode();
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
connect(&_myCamera, &Camera::captureMouseChanged, this, &Application::captureMouseChanged);
DependencyManager::get<PickManager>()->setShouldPickHUDOperator([]() { return DependencyManager::get<HMDScriptingInterface>()->isHMDMode(); });
DependencyManager::get<PickManager>()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) {
@ -3512,8 +3521,10 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("About", AboutUtil::getInstance());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -3623,10 +3634,13 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("About", AboutUtil::getInstance());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated.
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance());
surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance());
// This `module` context property is blank for the QML scripting interface so that we don't get log errors when importing
// certain JS files from both scripts (in the JS context) and QML (in the QML context).
surfaceContext->setContextProperty("module", "");
@ -3974,6 +3988,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (_firstRun.get()) {
if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) {
DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS);
Menu::getInstance()->triggerOption(MenuOption::HomeLocation);
}
if (!_overrideEntry) {
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
@ -4693,6 +4712,11 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const {
void Application::mouseMoveEvent(QMouseEvent* event) {
PROFILE_RANGE(app_input_mouse, __FUNCTION__);
if (_ignoreMouseMove) {
_ignoreMouseMove = false;
return;
}
maybeToggleMenuVisible(event);
auto& compositor = getApplicationCompositor();
@ -4738,7 +4762,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
}
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->mouseMoveEvent(event);
_keyboardMouseDevice->mouseMoveEvent(event, _captureMouse, _mouseCaptureTarget);
}
}
@ -5532,6 +5556,19 @@ void Application::loadSettings() {
}
getMyAvatar()->loadData();
auto bucketEnum = QMetaEnum::fromType<ExternalResource::Bucket>();
auto externalResource = ExternalResource::getInstance();
for (int i = 0; i < bucketEnum.keyCount(); i++) {
const char* keyName = bucketEnum.key(i);
QString setting("ExternalResource/");
setting += keyName;
auto bucket = static_cast<ExternalResource::Bucket>(bucketEnum.keyToValue(keyName));
Setting::Handle<QString> url(setting, externalResource->getBase(bucket));
externalResource->setBase(bucket, url.get());
}
_settingsLoaded = true;
}
@ -5548,6 +5585,22 @@ void Application::saveSettings() const {
Menu::getInstance()->saveSettings();
getMyAvatar()->saveData();
PluginManager::getInstance()->saveSettings();
// Don't save external resource paths until such time as there's UI to select or set alternatives. Otherwise new default
// values won't be used unless Interface.json entries are manually remove or Interface.json is deleted.
/*
auto bucketEnum = QMetaEnum::fromType<ExternalResource::Bucket>();
auto externalResource = ExternalResource::getInstance();
for (int i = 0; i < bucketEnum.keyCount(); i++) {
const char* keyName = bucketEnum.key(i);
QString setting("ExternalResource/");
setting += keyName;
auto bucket = static_cast<ExternalResource::Bucket>(bucketEnum.keyToValue(keyName));
Setting::Handle<QString> url(setting, externalResource->getBase(bucket));
url.set(externalResource->getBase(bucket));
}
*/
}
bool Application::importEntities(const QString& urlOrFilename, const bool isObservable, const qint64 callerId) {
@ -5910,6 +5963,20 @@ void Application::cameraModeChanged() {
cameraMenuChanged();
}
bool Application::shouldCaptureMouse() const {
return _captureMouse && _glWidget->isActiveWindow() && !ui::Menu::isSomeSubmenuShown();
}
void Application::captureMouseChanged(bool captureMouse) {
_captureMouse = captureMouse;
if (_captureMouse) {
_glWidget->setCursor(QCursor(Qt::BlankCursor));
} else {
_mouseCaptureTarget = QPointF(NAN, NAN);
_glWidget->unsetCursor();
}
}
void Application::changeViewAsNeeded(float boomLength) {
// Switch between first and third person views as needed
// This is called when the boom length has changed
@ -6260,6 +6327,15 @@ void Application::update(float deltaTime) {
PROFILE_ASYNC_END(app, "Scene Loading", "");
}
if (shouldCaptureMouse()) {
QPoint point = _glWidget->mapToGlobal(_glWidget->geometry().center());
if (QCursor::pos() != point) {
_mouseCaptureTarget = point;
_ignoreMouseMove = true;
QCursor::setPos(point);
}
}
auto myAvatar = getMyAvatar();
{
PerformanceTimer perfTimer("devices");
@ -6315,8 +6391,8 @@ void Application::update(float deltaTime) {
if (deltaTime > FLT_EPSILON && userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z) == 0.0f) {
myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_PITCH));
myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_YAW));
myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_PITCH));
myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_YAW));
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
}
}
@ -7084,8 +7160,9 @@ void Application::updateWindowTitle() const {
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
bool isMetaverseLoggedIn = accountManager->isLoggedIn();
bool hasDomainLogIn = domainAccountManager->hasLogIn();
bool isDomainLoggedIn = domainAccountManager->isLoggedIn();
QString authedDomain = domainAccountManager->getAuthedDomain();
QString authedDomainName = domainAccountManager->getAuthedDomainName();
QString buildVersion = " - Vircadia - "
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
@ -7102,9 +7179,9 @@ void Application::updateWindowTitle() const {
QString currentPlaceName;
if (isServerlessMode()) {
if (isInErrorState) {
currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
currentPlaceName = "Serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
} else {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
currentPlaceName = "Serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
}
} else {
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
@ -7115,20 +7192,23 @@ void Application::updateWindowTitle() const {
QString metaverseDetails;
if (isMetaverseLoggedIn) {
metaverseDetails = "Metaverse: Logged in as " + metaverseUsername;
metaverseDetails = " (Metaverse: Connected to " + MetaverseAPI::getCurrentMetaverseServerURL().toString() + " as " + metaverseUsername + ")";
} else {
metaverseDetails = "Metaverse: Not Logged In";
metaverseDetails = " (Metaverse: Not Logged In)";
}
QString domainDetails;
if (currentPlaceName == authedDomain && isDomainLoggedIn) {
domainDetails = "Domain: Logged in as " + domainUsername;
if (hasDomainLogIn) {
if (currentPlaceName == authedDomainName && isDomainLoggedIn) {
domainDetails = " (Domain: Logged in as " + domainUsername + ")";
} else {
domainDetails = " (Domain: Not Logged In)";
}
} else {
domainDetails = "Domain: Not Logged In";
domainDetails = "";
}
QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")"
+ buildVersion;
QString title = currentPlaceName + connectionStatus + metaverseDetails + domainDetails + buildVersion;
#ifndef WIN32
// crashes with vs2013/win32
@ -7520,7 +7600,8 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("About", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); // Deprecated.
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
registerInteractiveWindowMetaType(scriptEngine.data());
@ -7657,7 +7738,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
QUrl scriptURL { scriptFilenameOrURL };
if (scriptURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) {
if (scriptURL.host().endsWith(NetworkingConstants::HF_MARKETPLACE_CDN_HOSTNAME)) {
int startIndex = shortName.lastIndexOf('/') + 1;
int endIndex = shortName.lastIndexOf('?');
shortName = shortName.mid(startIndex, endIndex - startIndex);
@ -7780,7 +7861,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
const int MAX_CHARACTERS_PER_LINE = 90;
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
QUrl originURL { url };
if (originURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) {
if (originURL.host().endsWith(NetworkingConstants::HF_MARKETPLACE_CDN_HOSTNAME)) {
// Create a confirmation dialog when this call is made
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "

View file

@ -4,6 +4,7 @@
//
// Created by Andrzej Kapolka on 5/10/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -430,6 +431,7 @@ public slots:
void cycleCamera();
void cameraModeChanged();
void cameraMenuChanged();
void captureMouseChanged(bool captureMouse);
void toggleOverlays();
void setOverlaysVisible(bool visible);
Q_INVOKABLE void centerUI();
@ -602,6 +604,7 @@ private:
void maybeToggleMenuVisible(QMouseEvent* event) const;
void toggleTabletUI(bool shouldOpen = false) const;
bool shouldCaptureMouse() const;
void userKickConfirmation(const QUuid& nodeID);
@ -755,7 +758,9 @@ private:
bool _settingsLoaded { false };
bool _fakedMouseEvent { false };
bool _captureMouse { false };
bool _ignoreMouseMove { false };
QPointF _mouseCaptureTarget { NAN, NAN };
bool _isMissingSequenceNumbers { false };

View file

@ -4,6 +4,7 @@
//
// Created by Triplelexx on 23/03/17.
// Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -282,12 +283,14 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
const QString& avatarIcon = QString("");
const QVariant& avatarScale = myAvatar->getAvatarScale();
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
QVariantMap bookmark;
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark.insert(ENTRY_AVATAR_ICON, avatarIcon);
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
QVariantList wearableEntities;

View file

@ -4,6 +4,7 @@
//
// Created by Triplelexx on 23/03/17.
// Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -148,6 +149,7 @@ protected slots:
private:
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
const QString ENTRY_AVATAR_URL = "avatarUrl";
const QString ENTRY_AVATAR_ICON = "avatarIcon";
const QString ENTRY_AVATAR_ATTACHMENTS = "attachments";
const QString ENTRY_AVATAR_ENTITIES = "avatarEntites";
const QString ENTRY_AVATAR_SCALE = "avatarScale";

View file

@ -4,6 +4,7 @@
//
// Created by Gabriel Calero on 9/28/18.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,4 +13,4 @@
#pragma once
#include <QString>
static const QString API_SIGNUP_PATH = "api/v1/users";
static const QString API_SIGNUP_PATH = "/api/v1/users";

View file

@ -13,8 +13,10 @@
#define hifi_CrashHandler_h
#include <string>
class QCoreApplication;
bool startCrashHandler(std::string appPath);
void setCrashAnnotation(std::string name, std::string value);
void startCrashHookMonitor(QCoreApplication* app);
#endif // hifi_CrashHandler_h

View file

@ -81,4 +81,7 @@ void setCrashAnnotation(std::string name, std::string value) {
flushAnnotations();
}
void startCrashHookMonitor(QCoreApplication* app) {
}
#endif

View file

@ -15,13 +15,14 @@
#include <assert.h>
#include <mutex>
#include <vector>
#include <string>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QtCore/QAtomicInteger>
#include <QtCore/QDeadlineTimer>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
#include <QtCore/QString>
#if defined(__clang__)
#pragma clang diagnostic push
@ -31,7 +32,6 @@
#include <client/crashpad_client.h>
#include <client/crash_report_database.h>
#include <client/settings.h>
#include <client/annotation_list.h>
#include <client/crashpad_info.h>
#if defined(__clang__)
@ -42,37 +42,274 @@
#include <FingerprintUtils.h>
#include <UUID.h>
using namespace crashpad;
static const std::string BACKTRACE_URL{ CMAKE_BACKTRACE_URL };
static const std::string BACKTRACE_TOKEN{ CMAKE_BACKTRACE_TOKEN };
static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
// ------------------------------------------------------------------------------------------------
// SpinLock - a lock that can timeout attempting to lock a block of code, and is in a busy-wait cycle while trying to acquire
// note that this code will malfunction if you attempt to grab a lock while already holding it
CrashpadClient* client { nullptr };
std::mutex annotationMutex;
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
class SpinLock {
public:
SpinLock();
void lock();
bool lock(int msecs);
void unlock();
private:
QAtomicInteger<int> _lock{ 0 };
Q_DISABLE_COPY(SpinLock)
};
class SpinLockLocker {
public:
SpinLockLocker(SpinLock& lock, int msecs = -1);
~SpinLockLocker();
bool isLocked() const;
void unlock();
bool relock(int msecs = -1);
private:
SpinLock* _lock;
bool _isLocked;
Q_DISABLE_COPY(SpinLockLocker)
};
SpinLock::SpinLock() {
}
void SpinLock::lock() {
while (!_lock.testAndSetAcquire(0, 1))
;
}
bool SpinLock::lock(int msecs) {
QDeadlineTimer deadline(msecs);
for (;;) {
if (_lock.testAndSetAcquire(0, 1)) {
return true;
}
if (deadline.hasExpired()) {
return false;
}
}
}
void SpinLock::unlock() {
_lock.storeRelease(0);
}
SpinLockLocker::SpinLockLocker(SpinLock& lock, int msecs /* = -1 */ ) : _lock(&lock) {
_isLocked = _lock->lock(msecs);
}
SpinLockLocker::~SpinLockLocker() {
if (_isLocked) {
_lock->unlock();
}
}
bool SpinLockLocker::isLocked() const {
return _isLocked;
}
void SpinLockLocker::unlock() {
if (_isLocked) {
_lock->unlock();
_isLocked = false;
}
}
bool SpinLockLocker::relock(int msecs /* = -1 */ ) {
if (!_isLocked) {
_isLocked = _lock->lock(msecs);
}
return _isLocked;
}
// ------------------------------------------------------------------------------------------------
crashpad::CrashpadClient* client{ nullptr };
SpinLock crashpadAnnotationsProtect;
crashpad::SimpleStringDictionary* crashpadAnnotations{ nullptr };
#if defined(Q_OS_WIN)
static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler.exe" };
static const QString CRASHPAD_HANDLER_NAME{ "crashpad_handler.exe" };
#else
static const QString CRASHPAD_HANDLER_NAME { "crashpad_handler" };
static const QString CRASHPAD_HANDLER_NAME{ "crashpad_handler" };
#endif
#ifdef Q_OS_WIN
// ------------------------------------------------------------------------------------------------
// The area within this #ifdef is specific to the Microsoft C++ compiler
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QLogging.h>
#include <QtCore/QTimer>
#include <Windows.h>
#include <typeinfo>
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (!client) {
return EXCEPTION_CONTINUE_SEARCH;
}
static constexpr DWORD STATUS_MSVC_CPP_EXCEPTION = 0xE06D7363;
static constexpr ULONG_PTR MSVC_CPP_EXCEPTION_SIGNATURE = 0x19930520;
static constexpr int ANNOTATION_LOCK_WEAK_ATTEMPT = 5000; // attempt to lock the annotations list, but give up if it takes more than 5 seconds
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
LPTOP_LEVEL_EXCEPTION_FILTER gl_crashpadUnhandledExceptionFilter = nullptr;
QTimer unhandledExceptionTimer; // checks occasionally in case loading an external DLL reset the unhandled exception pointer
void fatalCxxException(PEXCEPTION_POINTERS pExceptionInfo); // extracts type information from a thrown C++ exception
LONG WINAPI firstChanceExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo); // called on any thrown exception (whether or not it's caught)
LONG WINAPI unhandledExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo); // called on any exception without a corresponding catch
static LONG WINAPI firstChanceExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
// we're catching these exceptions on first-chance as the system state is corrupted at this point and they may not survive the exception handling mechanism
if (client && (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN)) {
client->DumpAndCrash(pExceptionInfo);
}
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
static LONG WINAPI unhandledExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (client && pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_MSVC_CPP_EXCEPTION) {
fatalCxxException(pExceptionInfo);
client->DumpAndCrash(pExceptionInfo);
}
if (gl_crashpadUnhandledExceptionFilter != nullptr) {
return gl_crashpadUnhandledExceptionFilter(pExceptionInfo);
}
return EXCEPTION_CONTINUE_SEARCH;
}
// The following structures are modified versions of structs defined inplicitly by the Microsoft C++ compiler
// as described at http://www.geoffchappell.com/studies/msvc/language/predefined/
// They are redefined here as the definitions the compiler gives only work in 32-bit contexts and are out-of-sync
// with the internal structures when operating in a 64-bit environment
// as discovered and described here: https://stackoverflow.com/questions/39113168/c-rtti-in-a-windows-64-bit-vectoredexceptionhandler-ms-visual-studio-2015
#pragma pack(push, ehdata, 4)
struct PMD_internal { // internal name: _PMD (no changes, so could in theory just use the original)
int mdisp;
int pdisp;
int vdisp;
};
struct ThrowInfo_internal { // internal name: _ThrowInfo (changed all pointers into __int32)
__int32 attributes;
__int32 pmfnUnwind; // 32-bit RVA
__int32 pForwardCompat; // 32-bit RVA
__int32 pCatchableTypeArray; // 32-bit RVA
};
struct CatchableType_internal { // internal name: _CatchableType (changed all pointers into __int32)
__int32 properties;
__int32 pType; // 32-bit RVA
PMD_internal thisDisplacement;
__int32 sizeOrOffset;
__int32 copyFunction; // 32-bit RVA
};
#pragma warning(disable : 4200)
struct CatchableTypeArray_internal { // internal name: _CatchableTypeArray (changed all pointers into __int32)
int nCatchableTypes;
__int32 arrayOfCatchableTypes[0]; // 32-bit RVA
};
#pragma warning(default : 4200)
#pragma pack(pop, ehdata)
// everything inside this function is extremely undocumented, attempting to extract
// the underlying C++ exception type (or at least its name) before throwing the whole
// mess at crashpad
// Some links describing how C++ exception handling works in an SEH context
// (since C++ exceptions are a figment of the Microsoft compiler):
// - https://www.codeproject.com/Articles/175482/Compiler-Internals-How-Try-Catch-Throw-are-Interpr
// - https://stackoverflow.com/questions/21888076/how-to-find-the-context-record-for-user-mode-exception-on-x64
static void fatalCxxException(PEXCEPTION_POINTERS pExceptionInfo) {
SpinLockLocker guard(crashpadAnnotationsProtect, ANNOTATION_LOCK_WEAK_ATTEMPT);
if (!guard.isLocked()) {
return;
}
PEXCEPTION_RECORD ExceptionRecord = pExceptionInfo->ExceptionRecord;
/*
Exception arguments for Microsoft C++ exceptions:
[0] signature - magic number
[1] void* - variable that is being thrown
[2] ThrowInfo* - description of the variable that was thrown
[3] HMODULE - (64-bit only) base address that all 32bit pointers are added to
*/
if (ExceptionRecord->NumberParameters != 4 || ExceptionRecord->ExceptionInformation[0] != MSVC_CPP_EXCEPTION_SIGNATURE) {
// doesn't match expected parameter counts or magic numbers
return;
}
// get the ThrowInfo struct from the exception arguments
ThrowInfo_internal* pThrowInfo = reinterpret_cast<ThrowInfo_internal*>(ExceptionRecord->ExceptionInformation[2]);
ULONG_PTR moduleBase = ExceptionRecord->ExceptionInformation[3];
if (moduleBase == 0 || pThrowInfo == NULL) {
return; // broken assumption
}
// get the CatchableTypeArray* struct from ThrowInfo
if (pThrowInfo->pCatchableTypeArray == 0) {
return; // broken assumption
}
CatchableTypeArray_internal* pCatchableTypeArray =
reinterpret_cast<CatchableTypeArray_internal*>(moduleBase + pThrowInfo->pCatchableTypeArray);
if (pCatchableTypeArray->nCatchableTypes == 0 || pCatchableTypeArray->arrayOfCatchableTypes[0] == 0) {
return; // broken assumption
}
// get the CatchableType struct for the actual exception type from CatchableTypeArray
CatchableType_internal* pCatchableType =
reinterpret_cast<CatchableType_internal*>(moduleBase + pCatchableTypeArray->arrayOfCatchableTypes[0]);
if (pCatchableType->pType == 0) {
return; // broken assumption
}
const std::type_info* type = reinterpret_cast<std::type_info*>(moduleBase + pCatchableType->pType);
crashpadAnnotations->SetKeyValue("thrownObject", type->name());
// After annotating the name of the actual object type, go through the other entries in CatcahleTypeArray and itemize the list of possible
// catch() commands that could have caught this so we can find the list of its superclasses
QString compatibleObjects;
for (int catchTypeIdx = 1; catchTypeIdx < pCatchableTypeArray->nCatchableTypes; catchTypeIdx++) {
CatchableType_internal* pCatchableSuperclassType =
reinterpret_cast<CatchableType_internal*>(moduleBase + pCatchableTypeArray->arrayOfCatchableTypes[catchTypeIdx]);
if (pCatchableSuperclassType->pType == 0) {
return; // broken assumption
}
const std::type_info* superclassType = reinterpret_cast<std::type_info*>(moduleBase + pCatchableSuperclassType->pType);
if (!compatibleObjects.isEmpty()) {
compatibleObjects += ", ";
}
compatibleObjects += superclassType->name();
}
crashpadAnnotations->SetKeyValue("thrownObjectLike", compatibleObjects.toStdString());
}
void checkUnhandledExceptionHook() {
LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionHandler);
if (prevExceptionFilter != unhandledExceptionHandler) {
qWarning() << QString("Restored unhandled exception filter (which had been changed to %1)")
.arg(reinterpret_cast<ULONG_PTR>(prevExceptionFilter), 16, 16, QChar('0'));
}
}
// End of code specific to the Microsoft C++ compiler
// ------------------------------------------------------------------------------------------------
#endif // Q_OS_WIN
bool startCrashHandler(std::string appPath) {
if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) {
@ -80,7 +317,7 @@ bool startCrashHandler(std::string appPath) {
}
assert(!client);
client = new CrashpadClient();
client = new crashpad::CrashpadClient();
std::vector<std::string> arguments;
std::map<std::string, std::string> annotations;
@ -92,17 +329,16 @@ bool startCrashHandler(std::string appPath) {
auto machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
annotations["machine_fingerprint"] = machineFingerPrint.toStdString();
arguments.push_back("--no-rate-limit");
// Setup Crashpad DB directory
const auto crashpadDbName = "crashpad-db";
const auto crashpadDbDir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
QDir(crashpadDbDir).mkpath(crashpadDbName); // Make sure the directory exists
QDir(crashpadDbDir).mkpath(crashpadDbName); // Make sure the directory exists
const auto crashpadDbPath = crashpadDbDir.toStdString() + "/" + crashpadDbName;
// Locate Crashpad handler
const QFileInfo interfaceBinary { QString::fromStdString(appPath) };
const QFileInfo interfaceBinary{ QString::fromStdString(appPath) };
const QDir interfaceDir = interfaceBinary.dir();
assert(interfaceDir.exists(CRASHPAD_HANDLER_NAME));
const std::string CRASHPAD_HANDLER_PATH = interfaceDir.filePath(CRASHPAD_HANDLER_NAME).toStdString();
@ -124,18 +360,23 @@ bool startCrashHandler(std::string appPath) {
// Enable automated uploads.
database->GetSettings()->SetUploadsEnabled(true);
if (!client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true)) {
return false;
}
#ifdef Q_OS_WIN
AddVectoredExceptionHandler(0, vectoredExceptionHandler);
AddVectoredExceptionHandler(0, firstChanceExceptionHandler);
gl_crashpadUnhandledExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionHandler);
#endif
return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
return true;
}
void setCrashAnnotation(std::string name, std::string value) {
if (client) {
std::lock_guard<std::mutex> guard(annotationMutex);
SpinLockLocker guard(crashpadAnnotationsProtect);
if (!crashpadAnnotations) {
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
crashpadAnnotations = new crashpad::SimpleStringDictionary(); // don't free this, let it leak
crashpad::CrashpadInfo* crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
crashpad_info->set_simple_annotations(crashpadAnnotations);
}
@ -144,4 +385,18 @@ void setCrashAnnotation(std::string name, std::string value) {
}
}
#endif
void startCrashHookMonitor(QCoreApplication* app) {
#ifdef Q_OS_WIN
// create a timer that checks to see if our exception handler has been reset. This may occur when a new CRT
// is initialized, which could happen any time a DLL not compiled with the same compiler is loaded.
// It would be nice if this were replaced with a more intelligent response; this fires once a minute which
// may be too much (extra code running) and too little (leaving up to a 1min gap after the hook is broken)
checkUnhandledExceptionHook();
unhandledExceptionTimer.moveToThread(app->thread());
QObject::connect(&unhandledExceptionTimer, &QTimer::timeout, checkUnhandledExceptionHook);
unhandledExceptionTimer.start(60000);
#endif // Q_OS_WIN
}
#endif // HAS_CRASHPAD

View file

@ -25,4 +25,7 @@ bool startCrashHandler(std::string appPath) {
void setCrashAnnotation(std::string name, std::string value) {
}
void startCrashHookMonitor(QCoreApplication* app) {
}
#endif

View file

@ -36,6 +36,10 @@ class FancyCamera : public Camera {
* @property {ViewFrustum} frustum - The camera frustum.
* @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the
* camera is in entity mode.
* @property {boolean} captureMouse - The mouse capture state. When <code>true</code>, the mouse is invisible and cannot leave the bounds of
* Interface, as long as Interface is the active window and no menu item is selected. When <code>false</code>, the mouse
* behaves normally.
* @property {number} sensitivity - The current camera sensitivity. Must be positive.
*/
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)

View file

@ -43,6 +43,7 @@
#include "avatar/AvatarManager.h"
#include "avatar/AvatarPackager.h"
#include "AvatarBookmarks.h"
#include "DomainAccountManager.h"
#include "MainWindow.h"
#include "render/DrawStatus.h"
#include "scripting/MenuScriptingInterface.h"
@ -73,6 +74,7 @@ const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
Menu::Menu() {
auto dialogsManager = DependencyManager::get<DialogsManager>();
auto accountManager = DependencyManager::get<AccountManager>();
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
// File/Application menu ----------------------------------
MenuWrapper* fileMenu = addMenu("File");
@ -89,11 +91,15 @@ Menu::Menu() {
}
auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In");
domainLogin->setVisible(false);
connect(domainLogin, &QAction::triggered, [] {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->setDomainLoginState();
dialogsManager->showDomainLoginDialog();
});
connect(domainAccountManager.data(), &DomainAccountManager::hasLogInChanged, [domainLogin](bool hasLogIn) {
domainLogin->setVisible(hasLogIn);
});
// File > Quit
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);
@ -232,11 +238,11 @@ Menu::Menu() {
// Navigate > Start-up Location
MenuWrapper* startupLocationMenu = navigateMenu->addMenu(MenuOption::StartUpLocation);
QActionGroup* startupLocatiopnGroup = new QActionGroup(startupLocationMenu);
startupLocatiopnGroup->setExclusive(true);
startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0,
QActionGroup* startupLocationGroup = new QActionGroup(startupLocationMenu);
startupLocationGroup->setExclusive(true);
startupLocationGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::HomeLocation, 0,
false));
startupLocatiopnGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0,
startupLocationGroup->addAction(addCheckableActionToQMenuAndActionHash(startupLocationMenu, MenuOption::LastLocation, 0,
true));
// Settings menu ----------------------------------
@ -716,7 +722,7 @@ Menu::Menu() {
// Developer > Crash >>>
bool result = false;
const QString HIFI_SHOW_DEVELOPER_CRASH_MENU("HIFI_SHOW_DEVELOPER_CRASH_MENU");
result = QProcessEnvironment::systemEnvironment().contains(HIFI_SHOW_DEVELOPER_CRASH_MENU);
result = true;//QProcessEnvironment::systemEnvironment().contains(HIFI_SHOW_DEVELOPER_CRASH_MENU);
if (result) {
MenuWrapper* crashMenu = developerMenu->addMenu("Crash");
@ -756,6 +762,11 @@ Menu::Menu() {
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFaultThreaded);
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::newFault).join(); });
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashThrownException);
connect(action, &QAction::triggered, qApp, []() { crash::throwException(); });
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashThrownExceptionThreaded);
connect(action, &QAction::triggered, qApp, []() { std::thread(crash::throwException).join(); });
addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOnShutdown, 0, qApp, SLOT(crashOnShutdown()));
}

View file

@ -77,6 +77,8 @@ namespace MenuOption {
const QString CrashOutOfBoundsVectorAccessThreaded = "Out of Bounds Vector Access (threaded)";
const QString CrashNewFault = "New Fault";
const QString CrashNewFaultThreaded = "New Fault (threaded)";
const QString CrashThrownException = "Thrown C++ exception";
const QString CrashThrownExceptionThreaded = "Thrown C++ exception (threaded)";
const QString CreateEntitiesGrabbable = "Create Entities As Grabbable (except Zones, Particles, and Lights)";
const QString DeadlockInterface = "Deadlock Interface";
const QString UnresponsiveInterface = "Unresponsive Interface";

View file

@ -296,7 +296,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
mapping.insert(JOINT_FIELD, joints);
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
// then we can add the default mixamo to "faceshift" mappings
// then we can add the default mixamo to blendshape mappings.
if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) {
QVariantHash blendshapes;
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);

View file

@ -4,6 +4,7 @@
//
// Created by Mark Peng on 8/16/13.
// Copyright 2012 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -25,31 +26,33 @@
#include <scripting/HMDScriptingInterface.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <AnimDebugDraw.h>
#include <AnimClip.h>
#include <AnimInverseKinematics.h>
#include <AudioClient.h>
#include <ClientTraitsHandler.h>
#include <recording/Clip.h>
#include <recording/Deck.h>
#include <display-plugins/DisplayPlugin.h>
#include <recording/Frame.h>
#include <FSTReader.h>
#include <GeometryUtil.h>
#include <GLMHelpers.h>
#include <NodeList.h>
#include <NetworkingConstants.h>
#include <udt/PacketHeaders.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <SoundCache.h>
#include <ModelEntityItem.h>
#include <GLMHelpers.h>
#include <TextRenderer3D.h>
#include <UserActivityLogger.h>
#include <AnimDebugDraw.h>
#include <AnimClip.h>
#include <AnimInverseKinematics.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
#include <recording/Clip.h>
#include <recording/Frame.h>
#include <RecordingScriptingInterface.h>
#include <RenderableModelEntityItem.h>
#include <VariantMapToScriptValue.h>
#include <NetworkingConstants.h>
#include "MyHead.h"
#include "MySkeletonModel.h"
@ -82,7 +85,7 @@ const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
const int SCRIPTED_MOTOR_SIMPLE_MODE = 0;
const int SCRIPTED_MOTOR_DYNAMIC_MODE = 1;
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav";
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = NetworkingConstants::HF_PUBLIC_CDN_URL + "sounds/Collisions-otherorganic/Body_Hits_Impact.wav";
const float MyAvatar::ZOOM_MIN = 0.5f;
const float MyAvatar::ZOOM_MAX = 25.0f;

View file

@ -168,6 +168,7 @@ const btCollisionShape* OtherAvatar::createCollisionShape(int32_t jointIndex, bo
}
// Note: MultiSphereLow case really means: "skip fingers and use spheres for hands,
// else fall through to MultiSphereHigh case"
/* fall-thru */
case BodyLOD::MultiSphereHigh:
computeDetailedShapeInfo(shapeInfo, jointIndex);
break;

View file

@ -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>();

View file

@ -81,7 +81,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content <path>", "serverContentPath");
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
@ -381,6 +381,7 @@ int main(int argc, const char* argv[]) {
#if defined(Q_OS_LINUX)
app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/vircadia-logo.svg"));
#endif
startCrashHookMonitor(&app);
QTimer exitTimer;
if (traceDuration > 0.0f) {

View file

@ -30,7 +30,7 @@ QNetworkRequest createNetworkRequest() {
QNetworkRequest request;
QUrl requestURL = MetaverseAPI::getCurrentMetaverseServerURL();
requestURL.setPath(USER_ACTIVITY_URL);
requestURL.setPath(MetaverseAPI::getCurrentMetaverseServerURLPath() + USER_ACTIVITY_URL);
request.setUrl(requestURL);

View file

@ -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() { }

View file

@ -18,11 +18,13 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <DependencyManager.h>
#include <NetworkingConstants.h>
#include <NodeList.h>
#include <UUID.h>
#include "EntityScriptingInterface.h"
#include "ScreenshareScriptingInterface.h"
#include "ExternalResource.h"
static const int SCREENSHARE_INFO_REQUEST_RETRY_TIMEOUT_MS = 300;
ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
@ -42,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() {
@ -108,7 +111,7 @@ void ScreenshareScriptingInterface::requestScreenshareInfo() {
// See `DomainServer::screensharePresence()` for more info about that.
QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID());
QString requestURLPath = "api/v1/domains/%1/screenshare";
QString requestURLPath = "/api/v1/domains/%1/screenshare";
JSONCallbackParameters callbackParams;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet";
@ -128,8 +131,9 @@ static const uint8_t LOCAL_SCREENSHARE_WEB_ENTITY_FPS = 30;
// The `z` value here is dynamic.
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0128f, -0.0918f, 0.0f);
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(3.6790f, 2.0990f, 0.0100f);
static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL =
"https://content.highfidelity.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";
static const ExternalResource::Bucket LOCAL_SCREENSHARE_WEB_ENTITY_BUCKET = ExternalResource::Bucket::HF_Content;
static const QString LOCAL_SCREENSHARE_WEB_ENTITY_PATH =
"Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";
static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE = "local";
void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID,
const QUuid& smartboardEntityID,
@ -281,6 +285,8 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR
glm::vec3 localPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION);
localPosition.z = _localWebEntityZOffset;
localScreenshareWebEntityProps.setLocalPosition(localPosition);
auto LOCAL_SCREENSHARE_WEB_ENTITY_URL = ExternalResource::getInstance()->getUrl(LOCAL_SCREENSHARE_WEB_ENTITY_BUCKET,
LOCAL_SCREENSHARE_WEB_ENTITY_PATH);
localScreenshareWebEntityProps.setSourceUrl(LOCAL_SCREENSHARE_WEB_ENTITY_URL);
localScreenshareWebEntityProps.setParentID(_smartboardEntityID);
localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS);

View file

@ -20,6 +20,7 @@
#include <Trace.h>
#include "Application.h"
#include "NetworkingConstants.h"
Q_LOGGING_CATEGORY(trace_test, "trace.test")
@ -66,8 +67,8 @@ bool TestScriptingInterface::loadTestScene(QString scene) {
return result;
}
static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/";
static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/";
static const QString TEST_ROOT = "https://raw.githubusercontent.com/hifi-archive/hifi_tests/master/";
static const QString TEST_BINARY_ROOT = NetworkingConstants::HF_CONTENT_CDN_URL + "test_scene_data/";
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";

View file

@ -645,12 +645,16 @@ void WindowScriptingInterface::setActiveDisplayPlugin(int index) {
qApp->setActiveDisplayPlugin(name);
}
void WindowScriptingInterface::openWebBrowser() {
void WindowScriptingInterface::openWebBrowser(const QString& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "openWebBrowser", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "openWebBrowser", Q_ARG(const QString&, url));
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
if (!url.isEmpty()) {
newObject->setProperty("url", url);
}
});
}

View file

@ -616,10 +616,11 @@ public slots:
void setActiveDisplayPlugin(int index);
/**jsdoc
* Opens a web browser in a pop-up window.
* Opens an Interface web browser window.
* @function Window.openWebBrowser
* @param {string} [url=""] - The URL of the web page to display.
*/
void openWebBrowser();
void openWebBrowser(const QString& url = "");
private slots:

View file

@ -9,7 +9,8 @@
//
#include "DomainConnectionModel.h"
#include <QLoggingCategory>
#include <QtCore/QLoggingCategory>
#include <QtCore/QMetaMethod>
#include <NodeList.h>
#include <NumericalConstants.h>

View file

@ -167,6 +167,13 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
}
_extraDebuggingBox->show();
connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox);
_showSourceDebuggingBox = new QCheckBox("Show script sources", this);
if (_logger->showSourceDebugging()) {
_showSourceDebuggingBox->setCheckState(Qt::Checked);
}
_showSourceDebuggingBox->show();
connect(_showSourceDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleShowSourceDebuggingCheckbox);
_allLogsButton = new QPushButton("All Messages", this);
// set object name for css styling
@ -196,8 +203,13 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
_keepOnTopBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN,
_keepOnTopBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN,
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
_showSourceDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH
- ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ELEMENT_MARGIN
- COMBOBOX_WIDTH - ELEMENT_MARGIN,
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
@ -217,6 +229,8 @@ void LogDialog::handleRevealButton() {
}
void LogDialog::handleAllLogsButton() {
_logger->setShowSourceDebugging(false);
_showSourceDebuggingBox->setCheckState(Qt::Unchecked);
_logger->setExtraDebugging(false);
_extraDebuggingBox->setCheckState(Qt::Unchecked);
_logger->setDebugPrint(true);
@ -238,6 +252,10 @@ void LogDialog::handleAllLogsButton() {
printLogFile();
}
void LogDialog::handleShowSourceDebuggingCheckbox(int state) {
_logger->setShowSourceDebugging(state != 0);
}
void LogDialog::handleExtraDebuggingCheckbox(int state) {
_logger->setExtraDebugging(state != 0);
}

View file

@ -33,6 +33,7 @@ public slots:
private slots:
void handleRevealButton();
void handleShowSourceDebuggingCheckbox(int);
void handleExtraDebuggingCheckbox(int);
void handleKeepWindowOnTop(int);
void handleDebugPrintBox(int);
@ -55,6 +56,7 @@ protected:
private:
QCheckBox* _showSourceDebuggingBox;
QCheckBox* _extraDebuggingBox;
QCheckBox* _keepOnTopBox;
QPushButton* _revealLogButton;

View file

@ -172,7 +172,7 @@ void LoginDialog::linkOculus() {
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_OCULUS_PATH = "api/v1/user/oculus/link";
const QString LINK_OCULUS_PATH = "/api/v1/user/oculus/link";
QJsonObject payload;
payload["oculus_nonce"] = nonce;
@ -200,7 +200,7 @@ void LoginDialog::createAccountFromOculus(QString email, QString username, QStri
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_OCULUS_PATH = "api/v1/user/oculus/create";
const QString CREATE_ACCOUNT_FROM_OCULUS_PATH = "/api/v1/user/oculus/create";
QJsonObject payload;
payload["oculus_nonce"] = nonce;
@ -251,7 +251,7 @@ void LoginDialog::linkSteam() {
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
const QString LINK_STEAM_PATH = "/api/v1/user/steam/link";
QJsonObject payload;
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));
@ -278,7 +278,7 @@ void LoginDialog::createAccountFromSteam(QString username) {
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "/api/v1/user/steam/create";
QJsonObject payload;
payload["steam_auth_ticket"] = QJsonValue::fromVariant(QVariant(ticket));

Some files were not shown because too many files have changed in this diff Show more