diff --git a/.gitignore b/.gitignore index 8d92fe770b..09b58d71ef 100644 --- a/.gitignore +++ b/.gitignore @@ -17,10 +17,11 @@ Makefile local.properties android/gradle* android/.gradle -android/app/src/main/jniLibs -android/app/libs -android/app/src/main/res/values/libs.xml -android/app/src/main/assets/bundled +android/**/src/main/jniLibs +android/**/libs +android/**/src/main/res/values/libs.xml +android/**/src/main/assets +android/**/gradle* # VSCode # List taken from Github Global Ignores master@435c4d92 @@ -83,9 +84,6 @@ npm-debug.log # Android studio files *___jb_old___ -# Generated assets for Android -android/app/src/main/assets - # Resource binary file interface/compiledResources @@ -94,3 +92,11 @@ interface/resources/GPUCache/* # package lock file for JSDoc tool tools/jsdoc/package-lock.json + +# Python compile artifacts +**/__pycache__ + +# ignore unneeded unity project files for avatar exporter +tools/unity-avatar-exporter/Library +tools/unity-avatar-exporter/Packages +tools/unity-avatar-exporter/ProjectSettings diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 8c9263b6e7..c111e758b5 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -6,13 +6,8 @@ Building is currently supported on OSX, Windows and Linux platforms, but develop You will need the following tools to build Android targets. -* [Gradle](https://gradle.org/install/) * [Android Studio](https://developer.android.com/studio/index.html) -### Gradle - -Install gradle version 4.1 or higher. Following the instructions to install via [SDKMAN!](http://sdkman.io/install.html) is recommended. - ### Android Studio Download the Android Studio installer and run it. Once installed, at the welcome screen, click configure in the lower right corner and select SDK manager @@ -29,6 +24,8 @@ From the SDK Tools tab select the following * Android SDK Tools * NDK (even if you have the NDK installed separately) +Make sure the NDK installed version is 18 (or higher) + # Environment Setting up the environment for android builds requires some additional steps @@ -51,17 +48,17 @@ Enter the repository `android` directory `cd hifi/android` -Execute a gradle pre-build setup. This step should only need to be done once +Execute two gradle pre-build steps. This steps should only need to be done once, unless you're working on the Android dependencies -`gradle setupDependencies` +`./gradlew extractDependencies` +`./gradlew setupDependencies` # Building & Running * Open Android Studio * Choose _Open Existing Android Studio Project_ * Navigate to the `hifi` repository and choose the `android` folder and select _OK_ -* If Android Studio asks you if you want to use the Gradle wrapper, select cancel and tell it where your local gradle installation is. If you used SDKMAN to install gradle it will be located in `$HOME/.sdkman/candidates/gradle/current/` * From the _Build_ menu select _Make Project_ * Once the build completes, from the _Run_ menu select _Run App_ diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 15c5915c51..1559ece191 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -91,19 +91,73 @@ In a server, it does not make sense to compile interface ### Running the software +#### Domain server + Running domain server: ```bash ./domain-server/domain-server ``` +#### Assignment clients + Running assignment client: ```bash ./assignment-client/assignment-client -n 6 ``` +#### Interface + Running interface: ```bash ./interface/interface ``` Go to localhost in the running interface. + +##### Ubuntu 18.04 only + +In Ubuntu 18.04 there is a problem related with NVidia driver library version. + +It can be workarounded following these steps: + +Uninstall incompatible nvtt libraries: +```bash +sudo apt-get remove libnvtt2 libnvtt-dev +``` + +Install libssl1.0-dev: +```bash +sudo apt-get -y install libssl1.0-dev +``` + +Clone castano nvidia-texture-tools: +``` +git clone https://github.com/castano/nvidia-texture-tools +cd nvidia-texture-tools/ +``` + +Make these changes in repo: +* In file **VERSION** set `2.2.1` +* In file **configure**: + * set `build="release"` + * set `-DNVTT_SHARED=1` + +Configure, build and install: +``` +./configure +make +sudo make install +``` + +Link compiled files: +``` +sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so +sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so +sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so +sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so +``` + +After running this steps you can run interface: +``` +interface/interface +``` diff --git a/android/app/build.gradle b/android/app/build.gradle index cbc31479ec..e3c6989baf 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -139,23 +139,23 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.support:design:26.1.0' - compile 'com.android.support:support-v4:26.1.0' - compile 'com.android.support:appcompat-v7:26.1.0' - compile 'com.android.support:support-vector-drawable:26.1.0' + api 'com.android.support:support-v4:26.1.0' + api 'com.android.support:appcompat-v7:26.1.0' + api 'com.android.support:support-vector-drawable:26.1.0' implementation 'com.android.support:appcompat-v7:26.1.0' - compile 'com.android.support:recyclerview-v7:26.1.0' - compile 'com.android.support:cardview-v7:26.1.0' + api 'com.android.support:recyclerview-v7:26.1.0' + api 'com.android.support:cardview-v7:26.1.0' - compile 'com.squareup.retrofit2:retrofit:2.4.0' - compile 'com.squareup.retrofit2:converter-gson:2.4.0' + api 'com.squareup.retrofit2:retrofit:2.4.0' + api 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.picasso:picasso:2.71828' - compile 'com.squareup.retrofit2:retrofit:2.4.0' - compile 'com.squareup.retrofit2:converter-gson:2.4.0' + api 'com.squareup.retrofit2:retrofit:2.4.0' + api 'com.squareup.retrofit2:converter-gson:2.4.0' implementation 'com.squareup.picasso:picasso:2.71828' - compile 'com.sothree.slidinguppanel:library:3.4.0' + api 'com.sothree.slidinguppanel:library:3.4.0' implementation fileTree(include: ['*.jar'], dir: 'libs') } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 57e708068f..2ff35b6c3e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ - diff --git a/android/build.gradle b/android/build.gradle index e22c2d877f..8d03b9f6b3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.2.1' } } @@ -392,7 +392,7 @@ task extractDependencies(dependsOn: verifyDependencyDownloads) { } // Copies the non Qt dependencies. Qt dependencies (primary libraries and plugins) are handled by the qtBundle task -task copyDependencies(dependsOn: [ extractDependencies ]) { +task copyDependencies() { doLast { packages.each { entry -> def packageName = entry.key @@ -414,7 +414,7 @@ task copyDependencies(dependsOn: [ extractDependencies ]) { } } -task extractGvrBinaries(dependsOn: extractDependencies) { +task extractGvrBinaries() { doLast { def gvrLibFolder = new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries'); zipTree(new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries/sdk-audio-1.101.0.aar')).visit { element -> diff --git a/android/build_android.sh b/android/build_android.sh new file mode 100755 index 0000000000..f98bd1a4b2 --- /dev/null +++ b/android/build_android.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -xeuo pipefail +./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} setupDependencies +./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} app:${ANDROID_BUILD_TARGET} \ No newline at end of file diff --git a/android/containerized_build.sh b/android/containerized_build.sh new file mode 100755 index 0000000000..cd6f15a92e --- /dev/null +++ b/android/containerized_build.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -xeuo pipefail + +DOCKER_IMAGE_NAME="hifi_androidbuild" + +docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/Dockerfile docker + +docker run \ + --rm \ + --security-opt seccomp:unconfined \ + -v "${WORKSPACE}":/home/jenkins/hifi \ + -e "RELEASE_NUMBER=${RELEASE_NUMBER}" \ + -e "RELEASE_TYPE=${RELEASE_TYPE}" \ + -e "ANDROID_BUILD_TARGET=assembleDebug" \ + -e "CMAKE_BACKTRACE_URL=${CMAKE_BACKTRACE_URL}" \ + -e "CMAKE_BACKTRACE_TOKEN=${CMAKE_BACKTRACE_TOKEN}" \ + -e "CMAKE_BACKTRACE_SYMBOLS_TOKEN=${CMAKE_BACKTRACE_SYMBOLS_TOKEN}" \ + -e "GA_TRACKING_ID=${GA_TRACKING_ID}" \ + -e "GIT_PR_COMMIT=${GIT_PR_COMMIT}" \ + -e "VERSION_CODE=${VERSION_CODE}" \ + "${DOCKER_IMAGE_NAME}" \ + sh -c "./build_android.sh" diff --git a/android/docker/Dockerfile b/android/docker/Dockerfile new file mode 100644 index 0000000000..2a6943cbc2 --- /dev/null +++ b/android/docker/Dockerfile @@ -0,0 +1,92 @@ +FROM openjdk:8 + +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +RUN apt-get update && apt-get -y install \ + curl \ + gnupg \ + software-properties-common \ + unzip \ + - + +# --- Versions and Download paths +ENV ANDROID_HOME="/usr/local/android-sdk" \ + ANDROID_NDK_HOME="/usr/local/android-ndk" \ + ANDROID_SDK_HOME="/usr/local/android-sdk-home" \ + ANDROID_VERSION=26 \ + ANDROID_BUILD_TOOLS_VERSION=28.0.3 \ + ANDROID_NDK_VERSION=r18 + +ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" \ + NDK_URL="https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_VERSION}-linux-x86_64.zip" + +# --- Android SDK +RUN mkdir -p "$ANDROID_HOME" "$ANDROID_SDK_HOME" && \ + cd "$ANDROID_HOME" && \ + curl -s -S -o sdk.zip -L "${SDK_URL}" && \ + unzip sdk.zip && \ + rm sdk.zip && \ + yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses + +# Install Android Build Tool and Libraries +RUN $ANDROID_HOME/tools/bin/sdkmanager --update +RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \ + "platforms;android-${ANDROID_VERSION}" \ + "platform-tools" + +RUN chmod -R a+w "${ANDROID_HOME}" +RUN chmod -R a+w "${ANDROID_SDK_HOME}" + +# --- Android NDK +# download +RUN mkdir /usr/local/android-ndk-tmp && \ + cd /usr/local/android-ndk-tmp && \ + curl -s -S -o ndk.zip -L "${NDK_URL}" && \ + unzip -q ndk.zip && \ + mv ./android-ndk-${ANDROID_NDK_VERSION} ${ANDROID_NDK_HOME} && \ + cd ${ANDROID_NDK_HOME} && \ + rm -rf /usr/local/android-ndk-tmp + +ENV PATH ${PATH}:${ANDROID_NDK_HOME} + +RUN apt-get -y install \ + g++ \ + gcc \ + - + +# --- Gradle +ARG BUILD_UID=1001 +RUN useradd -ms /bin/bash -u $BUILD_UID jenkins +USER jenkins +WORKDIR /home/jenkins + +# Hifi dependencies +ENV HIFI_BASE="/home/jenkins/hifi_android" +ENV HIFI_ANDROID_PRECOMPILED="$HIFI_BASE/dependencies" +ENV HIFI_VCPKG_BASE="$HIFI_BASE/vcpkg" + +RUN mkdir "$HIFI_BASE" && \ + mkdir "$HIFI_VCPKG_BASE" && \ + mkdir "$HIFI_ANDROID_PRECOMPILED" + +RUN git clone https://github.com/jherico/hifi.git && \ + cd ~/hifi && \ + git checkout feature/build/gradle-wrapper + + +WORKDIR /home/jenkins/hifi + +RUN touch .test4 && \ + git fetch && git reset origin/feature/build/gradle-wrapper --hard + +RUN mkdir build + +# Pre-cache the vcpkg managed dependencies +WORKDIR /home/jenkins/hifi/build +RUN python3 ../prebuild.py --build-root `pwd` --android + +# Pre-cache the gradle dependencies +WORKDIR /home/jenkins/hifi/android +RUN ./gradlew -m tasks -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED +RUN ./gradlew extractDependencies -PHIFI_ANDROID_PRECOMPILED=$HIFI_ANDROID_PRECOMPILED + diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f6b961fd5a Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..36dba9b2f5 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Dec 01 08:32:47 PST 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000000..cccdd3d517 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100755 index 0000000000..f9553162f1 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index bd368ef7c2..cc2973f61d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -428,6 +428,10 @@ void Agent::executeScript() { using namespace recording; static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName()); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { + if (_shouldMuteRecordingAudio) { + return; + } + static quint16 audioSequenceNumber{ 0 }; QByteArray audio(frame->data); @@ -755,7 +759,11 @@ void Agent::processAgentAvatarAudio() { int16_t numAvailableSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; const int16_t* nextSoundOutput = NULL; - if (_avatarSound) { + if (_avatarSound && _avatarSound->isReady()) { + if (isPlayingRecording && !_shouldMuteRecordingAudio) { + _shouldMuteRecordingAudio = true; + } + auto audioData = _avatarSound->getAudioData(); nextSoundOutput = reinterpret_cast(audioData->rawData() + _numAvatarSoundSentBytes); @@ -781,6 +789,10 @@ void Agent::processAgentAvatarAudio() { _avatarSound.clear(); _numAvatarSoundSentBytes = 0; _flushEncoder = true; + + if (_shouldMuteRecordingAudio) { + _shouldMuteRecordingAudio = false; + } } } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 7d47c8e713..244f72e624 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -107,6 +107,7 @@ private: ResourceRequest* _pendingScriptRequest { nullptr }; bool _isListeningToAudioStream = false; SharedSoundPointer _avatarSound; + bool _shouldMuteRecordingAudio { false }; int _numAvatarSoundSentBytes = 0; bool _isAvatar = false; QTimer* _avatarIdentityTimer = nullptr; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index afbc68378a..8a86af384a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -110,6 +110,8 @@ public: void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; } bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } + bool getPrevRequestsDomainListData() { return _prevRequestsDomainListData; } + void setPrevRequestsDomainListData(bool requesting) { _prevRequestsDomainListData = requesting; } const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; } @@ -176,6 +178,7 @@ private: int _recentOtherAvatarsOutOfView { 0 }; QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. bool _requestsDomainListData { false }; + bool _prevRequestsDomainListData{ false }; AvatarTraits::TraitVersions _lastReceivedTraitVersions; TraitsCheckTimestamp _lastReceivedTraitsChange; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index a037f24345..cd9d164ef7 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -268,6 +268,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // When this is true, the AvatarMixer will send Avatar data to a client // about avatars they've ignored or that are out of view bool PALIsOpen = nodeData->getRequestsDomainListData(); + bool PALWasOpen = nodeData->getPrevRequestsDomainListData(); // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick(); @@ -392,6 +393,26 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime)); } + + // If Avatar A's PAL WAS open but is no longer open, AND + // Avatar A is ignoring Avatar B OR Avatar B is ignoring Avatar A... + // + // This is a bit heavy-handed still - there are cases where a kill packet + // will be sent when it doesn't need to be (but where it _should_ be OK to send). + // However, it's less heavy-handed than using `shouldIgnore`. + if (PALWasOpen && !PALIsOpen && + (destinationNode->isIgnoringNodeWithID(avatarNode->getUUID()) || + avatarNode->isIgnoringNodeWithID(destinationNode->getUUID()))) { + // ...send a Kill Packet to Node A, instructing Node A to kill Avatar B, + // then have Node A cleanup the killed Node B. + auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); + packet->write(avatarNode->getUUID().toRfc4122()); + packet->writePrimitive(KillAvatarReason::AvatarIgnored); + nodeList->sendPacket(std::move(packet), *destinationNode); + nodeData->cleanupKilledNode(avatarNode->getUUID(), avatarNode->getLocalID()); + } + + nodeData->setPrevRequestsDomainListData(PALIsOpen); } // loop through our sorted avatars and allocate our bandwidth to them accordingly diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 6188971e1c..a2e13a03be 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -29,7 +29,7 @@ ScriptableAvatar::ScriptableAvatar() { QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { _globalPosition = getWorldPosition(); - return AvatarData::toByteArrayStateful(dataDetail); + return AvatarData::toByteArrayStateful(dataDetail, dropFaceTracking); } diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index 3b8180e49e..d81de8b212 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -127,6 +127,10 @@ int64_t DomainContentBackupManager::getMostRecentBackupTimeInSecs(const QString& } void DomainContentBackupManager::setup() { + for (auto& rule : _backupRules) { + removeOldBackupVersions(rule); + } + auto backups = getAllBackups(); for (auto& backup : backups) { QFile backupFile { backup.absolutePath }; diff --git a/hifi_android.py b/hifi_android.py new file mode 100644 index 0000000000..e3944cda9a --- /dev/null +++ b/hifi_android.py @@ -0,0 +1,286 @@ +import hifi_utils +import json +import os +import platform +import re +import shutil +import xml.etree.ElementTree as ET +import functools + +print = functools.partial(print, flush=True) + +ANDROID_PACKAGE_URL = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' + +ANDROID_PACKAGES = { + 'qt' : { + 'file': 'qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz', + 'versionId': '3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN', + 'checksum': 'aa449d4bfa963f3bc9a9dfe558ba29df', + }, + 'bullet': { + 'file': 'bullet-2.88_armv8-libcpp.tgz', + 'versionId': 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg', + 'checksum': '81642779ccb110f8c7338e8739ac38a0', + }, + 'draco': { + 'file': 'draco_armv8-libcpp.tgz', + 'versionId': '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8', + 'checksum': '617a80d213a5ec69fbfa21a1f2f738cd', + }, + 'glad': { + 'file': 'glad_armv8-libcpp.zip', + 'versionId': 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY', + 'checksum': 'a8ee8584cf1ccd34766c7ddd9d5e5449', + }, + 'gvr': { + 'file': 'gvrsdk_v1.101.0.tgz', + 'versionId': 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r', + 'checksum': '57fd02baa069176ba18597a29b6b4fc7', + }, + 'nvtt': { + 'file': 'nvtt_armv8-libcpp.zip', + 'versionId': 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO', + 'checksum': 'eb46d0b683e66987190ed124aabf8910', + 'sharedLibFolder': 'lib', + 'includeLibs': ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'] + }, + 'oculus': { + 'file': 'ovr_sdk_mobile_1.19.0.zip', + 'versionId': 's_RN1vlEvUi3pnT7WPxUC4pQ0RJBs27y', + 'checksum': '98f0afb62861f1f02dd8110b31ed30eb', + 'sharedLibFolder': 'VrApi/Libs/Android/arm64-v8a/Release', + 'includeLibs': ['libvrapi.so'] + }, + 'openssl': { + 'file': 'openssl-1.1.0g_armv8.tgz', + 'versionId': 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW', + 'checksum': 'cabb681fbccd79594f65fcc266e02f32' + }, + 'polyvox': { + 'file': 'polyvox_armv8-libcpp.tgz', + 'versionId': 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92', + 'checksum': 'dba88b3a098747af4bb169e9eb9af57e', + 'sharedLibFolder': 'lib', + 'includeLibs': ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], + }, + 'tbb': { + 'file': 'tbb-2018_U1_armv8_libcpp.tgz', + 'versionId': 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB', + 'checksum': '20768f298f53b195e71b414b0ae240c4', + 'sharedLibFolder': 'lib/release', + 'includeLibs': ['libtbb.so', 'libtbbmalloc.so'], + }, + 'hifiAC': { + 'baseUrl': 'http://s3.amazonaws.com/hifi-public/dependencies/', + 'file': 'codecSDK-android_armv8-2.0.zip', + 'checksum': '1cbef929675818fc64c4101b72f84a6a' + }, + 'etc2comp': { + 'file': 'etc2comp-patched-armv8-libcpp.tgz', + 'versionId': 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU', + 'checksum': '14b02795d774457a33bbc60e00a786bc' + }, + 'breakpad': { + 'file': 'breakpad.tgz', + 'versionId': '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI', + 'checksum': 'ddcb23df336b08017042ba4786db1d9e', + 'sharedLibFolder': 'lib', + 'includeLibs': {'libbreakpad_client.a'} + } +} + +ANDROID_PLATFORM_PACKAGES = { + 'Darwin' : { + 'qt': { + 'file': 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz', + 'versionId': 'OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup', + 'checksum': 'c83cc477c08a892e00c71764dca051a0' + }, + }, + 'Windows' : { + 'qt': { + 'file': 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz', + 'versionId': 'JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT', + 'checksum': '0582191cc55431aa4f660848a542883e' + }, + } +} + +QT5_DEPS = [ + 'Qt5Concurrent', + 'Qt5Core', + 'Qt5Gui', + 'Qt5Multimedia', + 'Qt5Network', + 'Qt5OpenGL', + 'Qt5Qml', + 'Qt5Quick', + 'Qt5QuickControls2', + 'Qt5QuickTemplates2', + 'Qt5Script', + 'Qt5ScriptTools', + 'Qt5Svg', + 'Qt5WebChannel', + 'Qt5WebSockets', + 'Qt5Widgets', + 'Qt5XmlPatterns', + # Android specific + 'Qt5AndroidExtras', + 'Qt5WebView', +] + +def getPlatformPackages(): + result = ANDROID_PACKAGES.copy() + system = platform.system() + if system in ANDROID_PLATFORM_PACKAGES: + platformPackages = ANDROID_PLATFORM_PACKAGES[system] + result = { **result, **platformPackages } + return result + +def getPackageUrl(package): + url = ANDROID_PACKAGE_URL + if 'baseUrl' in package: + url = package['baseUrl'] + url += package['file'] + if 'versionId' in package: + url += '?versionId=' + package['versionId'] + return url + +def copyAndroidLibs(packagePath, appPath): + androidPackages = getPlatformPackages() + jniPath = os.path.join(appPath, 'src/main/jniLibs/arm64-v8a') + if not os.path.isdir(jniPath): + os.makedirs(jniPath) + for packageName in androidPackages: + package = androidPackages[packageName] + if 'sharedLibFolder' in package: + sharedLibFolder = os.path.join(packagePath, packageName, package['sharedLibFolder']) + if 'includeLibs' in package: + for lib in package['includeLibs']: + sourceFile = os.path.join(sharedLibFolder, lib) + destFile = os.path.join(jniPath, os.path.split(lib)[1]) + if not os.path.isfile(destFile): + print("Copying {}".format(lib)) + shutil.copy(sourceFile, destFile) + +class QtPackager: + def __init__(self, appPath, qtRootPath): + self.appPath = appPath + self.qtRootPath = qtRootPath + self.jniPath = os.path.join(self.appPath, 'src/main/jniLibs/arm64-v8a') + self.assetPath = os.path.join(self.appPath, 'src/main/assets') + self.qtAssetPath = os.path.join(self.assetPath, '--Added-by-androiddeployqt--') + # Jars go into the qt library + self.jarPath = os.path.realpath(os.path.join(self.appPath, '../../libraries/qt/libs')) + self.xmlFile = os.path.join(self.appPath, 'src/main/res/values/libs.xml') + self.files = [] + self.features = [] + self.permissions = [] + + def copyQtDeps(self): + for lib in QT5_DEPS: + libfile = os.path.join(self.qtRootPath, "lib/lib{}.so".format(lib)) + if not os.path.exists(libfile): + continue + self.files.append(libfile) + androidDeps = os.path.join(self.qtRootPath, "lib/{}-android-dependencies.xml".format(lib)) + if not os.path.exists(androidDeps): + continue + + tree = ET.parse(androidDeps) + root = tree.getroot() + for item in root.findall('./dependencies/lib/depends/*'): + if (item.tag == 'lib') or (item.tag == 'bundled'): + relativeFilename = item.attrib['file'] + if (relativeFilename.startswith('qml')): + continue + filename = os.path.join(self.qtRootPath, relativeFilename) + self.files.extend(hifi_utils.recursiveFileList(filename)) + elif item.tag == 'jar' and 'bundling' in item.attrib and item.attrib['bundling'] == "1": + self.files.append(os.path.join(self.qtRootPath, item.attrib['file'])) + elif item.tag == 'permission': + self.permissions.append(item.attrib['name']) + elif item.tag == 'feature': + self.features.append(item.attrib['name']) + + def scanQmlImports(self): + qmlImportCommandFile = os.path.join(self.qtRootPath, 'bin/qmlimportscanner') + system = platform.system() + if 'Windows' == system: + qmlImportCommandFile += ".exe" + if not os.path.isfile(qmlImportCommandFile): + raise RuntimeError("Couldn't find qml import scanner") + qmlRootPath = hifi_utils.scriptRelative('interface/resources/qml') + qmlImportPath = os.path.join(self.qtRootPath, 'qml') + commandResult = hifi_utils.executeSubprocessCapture([ + qmlImportCommandFile, + '-rootPath', qmlRootPath, + '-importPath', qmlImportPath + ]) + qmlImportResults = json.loads(commandResult) + for item in qmlImportResults: + if 'path' not in item: + print("Warning: QML import could not be resolved in any of the import paths: {}".format(item['name'])) + continue + path = os.path.realpath(item['path']) + if not os.path.exists(path): + continue + basePath = path + if os.path.isfile(basePath): + basePath = os.path.dirname(basePath) + basePath = os.path.normcase(basePath) + if basePath.startswith(qmlRootPath): + continue + self.files.extend(hifi_utils.recursiveFileList(path)) + + def processFiles(self): + self.files = list(set(self.files)) + self.files.sort() + libsXmlRoot = ET.Element('resources') + qtLibsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'qt_libs'}) + bundledLibsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'bundled_in_lib'}) + bundledAssetsNode = ET.SubElement(libsXmlRoot, 'array', {'name':'bundled_in_assets'}) + libPrefix = 'lib' + for sourceFile in self.files: + if not os.path.isfile(sourceFile): + raise RuntimeError("Unable to find dependency file " + sourceFile) + relativePath = os.path.relpath(sourceFile, self.qtRootPath) + destinationFile = None + if relativePath.endswith('.so'): + garbledFileName = None + if relativePath.startswith(libPrefix): + garbledFileName = relativePath[4:] + p = re.compile(r'lib(Qt5.*).so') + m = p.search(garbledFileName) + if not m: + raise RuntimeError("Huh?") + libName = m.group(1) + ET.SubElement(qtLibsNode, 'item').text = libName + else: + garbledFileName = 'lib' + relativePath.replace('\\', '_'[0]) + value = "{}:{}".format(garbledFileName, relativePath).replace('\\', '/') + ET.SubElement(bundledLibsNode, 'item').text = value + destinationFile = os.path.join(self.jniPath, garbledFileName) + elif relativePath.startswith('jar'): + destinationFile = os.path.join(self.jarPath, relativePath[4:]) + else: + value = "--Added-by-androiddeployqt--/{}:{}".format(relativePath,relativePath).replace('\\', '/') + ET.SubElement(bundledAssetsNode, 'item').text = value + destinationFile = os.path.join(self.qtAssetPath, relativePath) + + destinationParent = os.path.realpath(os.path.dirname(destinationFile)) + if not os.path.isdir(destinationParent): + os.makedirs(destinationParent) + if not os.path.isfile(destinationFile): + shutil.copy(sourceFile, destinationFile) + + tree = ET.ElementTree(libsXmlRoot) + tree.write(self.xmlFile, 'UTF-8', True) + + def bundle(self): + if not os.path.isfile(self.xmlFile) or True: + self.copyQtDeps() + self.scanQmlImports() + self.processFiles() + + diff --git a/hifi_singleton.py b/hifi_singleton.py new file mode 100644 index 0000000000..692948c80b --- /dev/null +++ b/hifi_singleton.py @@ -0,0 +1,46 @@ +import os +import platform +import time + +try: + import fcntl +except ImportError: + fcntl = None + +# Used to ensure only one instance of the script runs at a time +class Singleton: + def __init__(self, path): + self.fh = None + self.windows = 'Windows' == platform.system() + self.path = path + + def __enter__(self): + success = False + while not success: + try: + if self.windows: + if os.path.exists(self.path): + os.unlink(self.path) + self.fh = os.open(self.path, os.O_CREAT | os.O_EXCL | os.O_RDWR) + else: + self.fh = open(self.path, 'x') + fcntl.lockf(self.fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + success = True + except EnvironmentError as err: + if self.fh is not None: + if self.windows: + os.close(self.fh) + else: + self.fh.close() + self.fh = None + print("Couldn't aquire lock, retrying in 10 seconds") + time.sleep(10) + return self + + def __exit__(self, type, value, traceback): + if self.windows: + os.close(self.fh) + else: + fcntl.lockf(self.fh, fcntl.LOCK_UN) + self.fh.close() + os.unlink(self.path) \ No newline at end of file diff --git a/hifi_utils.py b/hifi_utils.py new file mode 100644 index 0000000000..f53258d4f6 --- /dev/null +++ b/hifi_utils.py @@ -0,0 +1,124 @@ +import os +import hashlib +import platform +import shutil +import ssl +import subprocess +import sys +import tarfile +import urllib +import urllib.request +import zipfile +import tempfile +import time +import functools + +print = functools.partial(print, flush=True) + +def scriptRelative(*paths): + scriptdir = os.path.dirname(os.path.realpath(sys.argv[0])) + result = os.path.join(scriptdir, *paths) + result = os.path.realpath(result) + result = os.path.normcase(result) + return result + + +def recursiveFileList(startPath): + result = [] + if os.path.isfile(startPath): + result.append(startPath) + elif os.path.isdir(startPath): + for dirName, subdirList, fileList in os.walk(startPath): + for fname in fileList: + result.append(os.path.realpath(os.path.join(startPath, dirName, fname))) + result.sort() + return result + + +def executeSubprocessCapture(processArgs): + processResult = subprocess.run(processArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if (0 != processResult.returncode): + raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n\nstdout:\n{}\n\nstderr:\n{}'.format( + processArgs[0], + ' '.join(processArgs[1:]), + processResult.stdout.decode('utf-8'), + processResult.stderr.decode('utf-8'))) + return processResult.stdout.decode('utf-8') + +def executeSubprocess(processArgs, folder=None, env=None): + restoreDir = None + if folder != None: + restoreDir = os.getcwd() + os.chdir(folder) + + process = subprocess.Popen( + processArgs, stdout=sys.stdout, stderr=sys.stderr, env=env) + process.wait() + + if (0 != process.returncode): + raise RuntimeError('Call to "{}" failed.\n\narguments:\n{}\n'.format( + processArgs[0], + ' '.join(processArgs[1:]), + )) + + if restoreDir != None: + os.chdir(restoreDir) + + +def hashFile(file, hasher = hashlib.sha512()): + with open(file, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hasher.update(chunk) + return hasher.hexdigest() + +# Assumes input files are in deterministic order +def hashFiles(filenames): + hasher = hashlib.sha256() + for filename in filenames: + with open(filename, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hasher.update(chunk) + return hasher.hexdigest() + +def hashFolder(folder): + filenames = recursiveFileList(folder) + return hashFiles(filenames) + +def downloadFile(url, hash=None, hasher=hashlib.sha512(), retries=3): + for i in range(retries): + tempFileName = None + # OSX Python doesn't support SSL, so we need to bypass it. + # However, we still validate the downloaded file's sha512 hash + if 'Darwin' == platform.system(): + tempFileDescriptor, tempFileName = tempfile.mkstemp() + context = ssl._create_unverified_context() + with urllib.request.urlopen(url, context=context) as response, open(tempFileDescriptor, 'wb') as tempFile: + shutil.copyfileobj(response, tempFile) + else: + tempFileName, headers = urllib.request.urlretrieve(url) + + # for some reason the hash we get back from the downloaded file is sometimes wrong if we check it right away + # but if we examine the file later, it is correct. + time.sleep(3) + downloadHash = hashFile(tempFileName, hasher) + # Verify the hash + if hash is not None and hash != downloadHash: + print("Try {}: Downloaded file {} hash {} does not match expected hash {} for url {}".format(i + 1, tempFileName, downloadHash, hash, url)) + os.remove(tempFileName) + continue + + return tempFileName + + raise RuntimeError("Downloaded file hash {} does not match expected hash {} for\n{}".format(downloadHash, hash, url)) + + +def downloadAndExtract(url, destPath, hash=None, hasher=hashlib.sha512(), isZip=False): + tempFileName = downloadFile(url, hash, hasher) + if isZip: + with zipfile.ZipFile(tempFileName) as zip: + zip.extractall(destPath) + else: + # Extract the archive + with tarfile.open(tempFileName, 'r:gz') as tgz: + tgz.extractall(destPath) + os.remove(tempFileName) diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py new file mode 100644 index 0000000000..5492109864 --- /dev/null +++ b/hifi_vcpkg.py @@ -0,0 +1,216 @@ +import hifi_utils +import hifi_android +import hashlib +import os +import platform +import re +import shutil +import tempfile +import json +import xml.etree.ElementTree as ET +import functools + +print = functools.partial(print, flush=True) + +# Encapsulates the vcpkg system +class VcpkgRepo: + CMAKE_TEMPLATE = """ +set(CMAKE_TOOLCHAIN_FILE "{}" CACHE FILEPATH "Toolchain file") +set(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}") +set(VCPKG_INSTALL_ROOT "{}") +set(VCPKG_TOOLS_DIR "{}") +""" + + CMAKE_TEMPLATE_NON_ANDROID = """ +# If the cached cmake toolchain path is different from the computed one, exit +if(NOT (CMAKE_TOOLCHAIN_FILE_UNCACHED STREQUAL CMAKE_TOOLCHAIN_FILE)) + message(FATAL_ERROR "CMAKE_TOOLCHAIN_FILE has changed, please wipe the build directory and rerun cmake") +endif() +""" + + def __init__(self, args): + self.args = args + # our custom ports, relative to the script location + self.sourcePortsPath = args.ports_path + self.id = hifi_utils.hashFolder(self.sourcePortsPath)[:8] + self.configFilePath = os.path.join(args.build_root, 'vcpkg.cmake') + + # OS dependent information + system = platform.system() + + if self.args.vcpkg_root is not None: + self.path = args.vcpkg_root + else: + if 'Darwin' == system: + defaultBasePath = os.path.expanduser('~/hifi/vcpkg') + else: + defaultBasePath = os.path.join(tempfile.gettempdir(), 'hifi', 'vcpkg') + self.basePath = os.getenv('HIFI_VCPKG_BASE', defaultBasePath) + if self.basePath == defaultBasePath: + print("Warning: Environment variable HIFI_VCPKG_BASE not set, using {}".format(defaultBasePath)) + if self.args.android: + self.basePath = os.path.join(self.basePath, 'android') + if (not os.path.isdir(self.basePath)): + os.makedirs(self.basePath) + self.path = os.path.join(self.basePath, self.id) + + print("Using vcpkg path {}".format(self.path)) + lockDir, lockName = os.path.split(self.path) + lockName += '.lock' + if not os.path.isdir(lockDir): + os.makedirs(lockDir) + + self.lockFile = os.path.join(lockDir, lockName) + self.tagFile = os.path.join(self.path, '.id') + # A format version attached to the tag file... increment when you want to force the build systems to rebuild + # without the contents of the ports changing + self.version = 1 + self.tagContents = "{}_{}".format(self.id, self.version) + + if 'Windows' == system: + self.exe = os.path.join(self.path, 'vcpkg.exe') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-win32.tar.gz?versionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ' + self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e' + self.hostTriplet = 'x64-windows' + elif 'Darwin' == system: + self.exe = os.path.join(self.path, 'vcpkg') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-osx.tar.gz?versionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc' + self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d' + self.hostTriplet = 'x64-osx' + else: + self.exe = os.path.join(self.path, 'vcpkg') + self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-linux.tar.gz?versionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU' + self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' + self.hostTriplet = 'x64-linux' + + if self.args.android: + self.triplet = 'arm64-android' + self.androidPackagePath = os.path.join(self.path, 'android') + else: + self.triplet = self.hostTriplet + + def upToDate(self): + # Prevent doing a clean if we've explcitly set a directory for vcpkg + if self.args.vcpkg_root is not None: + return True + + if self.args.force_build: + print("Force build, out of date") + return False + if not os.path.isfile(self.exe): + print("Exe file {} not found, out of date".format(self.exe)) + return False + if not os.path.isfile(self.tagFile): + print("Tag file {} not found, out of date".format(self.tagFile)) + return False + with open(self.tagFile, 'r') as f: + storedTag = f.read() + if storedTag != self.tagContents: + print("Tag file {} contents don't match computed tag {}, out of date".format(self.tagFile, self.tagContents)) + return False + return True + + def clean(self): + print("Cleaning vcpkg installation at {}".format(self.path)) + if os.path.isdir(self.path): + print("Removing {}".format(self.path)) + shutil.rmtree(self.path, ignore_errors=True) + + # Make sure the VCPKG prerequisites are all there. + def bootstrap(self): + if self.upToDate(): + return + + self.clean() + + downloadVcpkg = False + if self.args.force_bootstrap: + print("Forcing bootstrap") + downloadVcpkg = True + + if not downloadVcpkg and not os.path.isfile(self.exe): + print("Missing executable, boostrapping") + downloadVcpkg = True + + # Make sure we have a vcpkg executable + testFile = os.path.join(self.path, '.vcpkg-root') + if not downloadVcpkg and not os.path.isfile(testFile): + print("Missing {}, bootstrapping".format(testFile)) + downloadVcpkg = True + + if downloadVcpkg: + print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path)) + hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash) + + print("Replacing port files") + portsPath = os.path.join(self.path, 'ports') + if (os.path.islink(portsPath)): + os.unlink(portsPath) + if (os.path.isdir(portsPath)): + shutil.rmtree(portsPath, ignore_errors=True) + shutil.copytree(self.sourcePortsPath, portsPath) + + def run(self, commands): + actualCommands = [self.exe, '--vcpkg-root', self.path] + actualCommands.extend(commands) + print("Running command") + print(actualCommands) + hifi_utils.executeSubprocess(actualCommands, folder=self.path) + + def setupDependencies(self): + # Special case for android, grab a bunch of binaries + # FIXME remove special casing for android builds eventually + if self.args.android: + print("Installing Android binaries") + self.setupAndroidDependencies() + + print("Installing host tools") + self.run(['install', '--triplet', self.hostTriplet, 'hifi-host-tools']) + + # If not android, install the hifi-client-deps libraries + if not self.args.android: + print("Installing build dependencies") + self.run(['install', '--triplet', self.triplet, 'hifi-client-deps']) + + def cleanBuilds(self): + # Remove temporary build artifacts + builddir = os.path.join(self.path, 'buildtrees') + if os.path.isdir(builddir): + print("Wiping build trees") + shutil.rmtree(builddir, ignore_errors=True) + + def setupAndroidDependencies(self): + # vcpkg prebuilt + if not os.path.isdir(os.path.join(self.path, 'installed', 'arm64-android')): + dest = os.path.join(self.path, 'installed') + url = "https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-arm64-android.tar.gz" + # FIXME I don't know why the hash check frequently fails here. If you examine the file later it has the right hash + #hash = "832f82a4d090046bdec25d313e20f56ead45b54dd06eee3798c5c8cbdd64cce4067692b1c3f26a89afe6ff9917c10e4b601c118bea06d23f8adbfe5c0ec12bc3" + #hifi_utils.downloadAndExtract(url, dest, hash) + hifi_utils.downloadAndExtract(url, dest) + + def writeTag(self): + print("Writing tag {} to {}".format(self.tagContents, self.tagFile)) + with open(self.tagFile, 'w') as f: + f.write(self.tagContents) + + def writeConfig(self): + print("Writing cmake config to {}".format(self.configFilePath)) + # Write out the configuration for use by CMake + cmakeScript = os.path.join(self.path, 'scripts/buildsystems/vcpkg.cmake') + installPath = os.path.join(self.path, 'installed', self.triplet) + toolsPath = os.path.join(self.path, 'installed', self.hostTriplet, 'tools') + cmakeTemplate = VcpkgRepo.CMAKE_TEMPLATE + if not self.args.android: + cmakeTemplate += VcpkgRepo.CMAKE_TEMPLATE_NON_ANDROID + cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath).replace('\\', '/') + with open(self.configFilePath, 'w') as f: + f.write(cmakeConfig) + + def cleanOldBuilds(self): + # FIXME because we have the base directory, and because a build will + # update the tag file on every run, we can scan the base dir for sub directories containing + # a tag file that is older than N days, and if found, delete the directory, recovering space + print("Not implemented") + + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index e6d83e7890..c013cfacd3 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -206,7 +206,7 @@ endif() link_hifi_libraries( shared workload task octree ktx gpu gl procedural graphics graphics-scripting render pointers - recording hfm fbx networking model-networking entities avatars trackers + recording hfm fbx networking model-networking model-baker entities avatars trackers audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui qml auto-updater midi controllers plugins image trackers diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json index b3688ef06e..e16c6156ab 100644 --- a/interface/resources/config/keyboard.json +++ b/interface/resources/config/keyboard.json @@ -1,4 +1,22 @@ { + "backPlate": { + "dimensions": { + "x": 0.723600000888109207, + "y": 0.022600000724196434, + "z": 0.2474999976158142 + }, + "position": { + "x": -0.3292800903320312, + "y": 0.004300000742077827, + "z": -0.055427663803100586 + }, + "rotation": { + "w": 1.000, + "x": 0.000, + "y": 0.000, + "z": 0.000 + } + }, "anchor": { "dimensions": { "x": 0.023600000888109207, diff --git a/interface/resources/controllers/standard_nomovement.json b/interface/resources/controllers/standard_nomovement.json new file mode 100644 index 0000000000..015bc33056 --- /dev/null +++ b/interface/resources/controllers/standard_nomovement.json @@ -0,0 +1,78 @@ +{ + "name": "Standard to Action (No Movement)", + "channels": [ + { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, + { "from": "Standard.RT", "to": "Actions.RightHandClick" }, + + { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"}, + { "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"}, + { "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"}, + { "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"}, + { "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"}, + { "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"}, + { "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"}, + { "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"}, + { "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"}, + { "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"}, + { "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"}, + { "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"}, + { "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"}, + { "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"}, + { "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"}, + { "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"}, + { "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"}, + { "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"}, + { "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"}, + { "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"}, + + { "from": "Standard.RightHand", "to": "Actions.RightHand" }, + { "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"}, + { "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"}, + { "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"}, + { "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"}, + { "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"}, + { "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"}, + { "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"}, + { "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"}, + { "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"}, + { "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"}, + { "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"}, + { "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"}, + { "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"}, + { "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"}, + { "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"}, + { "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"}, + { "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"}, + { "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"}, + { "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"}, + { "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"}, + + { "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" }, + { "from": "Standard.RightFoot", "to": "Actions.RightFoot" }, + + { "from": "Standard.Hips", "to": "Actions.Hips" }, + { "from": "Standard.Spine2", "to": "Actions.Spine2" }, + + { "from": "Standard.Head", "to": "Actions.Head" }, + { "from": "Standard.LeftArm", "to": "Actions.LeftArm" }, + { "from": "Standard.RightArm", "to": "Actions.RightArm" }, + + { "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" }, + { "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" }, + { "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" }, + { "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" }, + { "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" }, + { "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" }, + { "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" }, + { "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" }, + { "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" }, + { "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" }, + { "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" }, + { "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" }, + { "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" }, + { "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" }, + { "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" }, + { "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" } + ] +} diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff deleted file mode 100644 index 534f8e5623..0000000000 Binary files a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff and /dev/null differ diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.eot b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.eot similarity index 91% rename from interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.eot rename to interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.eot index 93f4bc89a4..6cdc2487a6 100644 Binary files a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.eot and b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.eot differ diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg similarity index 98% rename from interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg rename to interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg index 4f1fb2e690..de91dcae71 100644 --- a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg +++ b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg @@ -154,4 +154,6 @@ + + diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.ttf similarity index 91% rename from interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.ttf rename to interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.ttf index e85c193fd0..c27553333b 100644 Binary files a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff new file mode 100644 index 0000000000..6d46970136 Binary files /dev/null and b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff differ diff --git a/interface/resources/fonts/hifi-glyphs-1.32/icons-reference.html b/interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html similarity index 98% rename from interface/resources/fonts/hifi-glyphs-1.32/icons-reference.html rename to interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html index a6ffbd5436..1c75e9e235 100644 --- a/interface/resources/fonts/hifi-glyphs-1.32/icons-reference.html +++ b/interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html @@ -603,6 +603,14 @@
+
  • +
    + +
  • +
  • +
    + +
  • Character mapping

      @@ -1194,6 +1202,14 @@
      +
    • +
      + +
    • +
    • +
      + +