mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into macOS_fix_fullscreen_crash_GHA
This commit is contained in:
commit
1a82c64668
99 changed files with 3483 additions and 623 deletions
4
.github/workflows/master_build.yml
vendored
4
.github/workflows/master_build.yml
vendored
|
@ -58,7 +58,7 @@ jobs:
|
|||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=tgz" >> $GITHUB_ENV
|
||||
echo "CMAKE_BUILD_EXTRA=-- -j3" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
fi
|
||||
# Mac build variables
|
||||
if [ "${{ matrix.os }}" = "macOS-10.15" ]; then
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
echo "ZIP_COMMAND=zip" >> $GITHUB_ENV
|
||||
echo "ZIP_ARGS=-r" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip"
|
||||
echo "APP_TARGET_NAME=Vircadia" >> $GITHUB_ENV
|
||||
fi
|
||||
|
|
8
.github/workflows/pr_build.yml
vendored
8
.github/workflows/pr_build.yml
vendored
|
@ -77,9 +77,9 @@ jobs:
|
|||
echo "VCPKG_FORCE_SYSTEM_BINARIES=true" >> $GITHUB_ENV
|
||||
fi
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo "CMAKE_EXTRA=-DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CMAKE_EXTRA=-DCLIENT_ONLY=1 -DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DCLIENT_ONLY=1 -DBUILD_TOOLS:BOOLEAN=FALSE -DHIFI_PYTHON_EXEC:FILEPATH=$(which python3)" >> $GITHUB_ENV
|
||||
fi
|
||||
fi
|
||||
# Mac build variables
|
||||
|
@ -87,9 +87,9 @@ jobs:
|
|||
echo "PYTHON_EXEC=python3" >> $GITHUB_ENV
|
||||
echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV
|
||||
if [ "${{ matrix.build_type }}" = "full" ]; then
|
||||
echo "CMAKE_EXTRA=-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
else
|
||||
echo "CMAKE_EXTRA=-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCLIENT_ONLY=1 -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
echo "CMAKE_EXTRA=-DVIRCADIA_CPU_ARCHITECTURE= -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCLIENT_ONLY=1 -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "APP_TARGET_NAME=Vircadia" >> $GITHUB_ENV
|
||||
fi
|
||||
|
|
7
BUILD.md
7
BUILD.md
|
@ -192,6 +192,13 @@ The following build options can be used when running CMake
|
|||
* CLIENT_ONLY // Will package only the Interface
|
||||
* SERVER_ONLY // Will package only the Server
|
||||
|
||||
### Optimization build options
|
||||
|
||||
* VIRCADIA_OPTIMIZE - This variable defaults to 1 if not set and enables compiler optimization flags on Linux and MacOS. Setting it to 0 will result in unoptimized build.
|
||||
* VIRCADIA_CPU_ARCHITECTURE - This variable contains architecture specific compiler flags which are used if `VIRCADIA_OPTIMIZE` is true. If it is not set, it defaults to `-march=native -mtune=native`, which helps yield more performance for locally used build, but for packaging it needs to be set to different value for portability, for example `-msse3`. Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield
|
||||
maximum compatibility.
|
||||
|
||||
|
||||
### Developer Build Options
|
||||
|
||||
* USE_GLES
|
||||
|
|
|
@ -171,3 +171,11 @@ If your goal is to set up a development environment, it is desirable to set the
|
|||
directory that vcpkg builds into with the `HIFI_VCPKG_BASE` environment variable.
|
||||
For example, you might set `HIFI_VCPKG_BASE` to `/home/$USER/vcpkg`.
|
||||
By default, vcpkg will build in the system `/tmp` directory.
|
||||
|
||||
If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE`
|
||||
CMake variable needs to be set to architecture specific value.
|
||||
It defaults to `-march=native -mtune=native`, which yields builds optimized for particular
|
||||
machine, but builds will not work on machines lacking same CPU instructions.
|
||||
For packaging and AppImage it is recommended to set it to different value, for example `-msse3`.
|
||||
Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield
|
||||
maximum compatibility.
|
||||
|
|
67
BUILD_OSX.md
67
BUILD_OSX.md
|
@ -1,63 +1,84 @@
|
|||
# Build OSX
|
||||
# Build MacOS
|
||||
|
||||
*Last Updated on January 16, 2021*
|
||||
*Last Updated on October 19, 2021*
|
||||
|
||||
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.
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. This will include the necessary environment variables to customize your build. Only macOS specific instructions are found in this document.
|
||||
|
||||
## Homebrew
|
||||
## Prerequisites
|
||||
|
||||
[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes install of some Vircadia dependencies very simple.
|
||||
### CMake, OpenSSL, and NPM
|
||||
|
||||
[Homebrew](https://brew.sh/) is an excellent package manager for macOS. It makes the installation of some Vircadia dependencies very simple.
|
||||
|
||||
```bash
|
||||
brew install cmake openssl npm
|
||||
```
|
||||
|
||||
Note: cmake versions > 3.18.x have known problems building Vircadia, so alternatively you can download cmake 3.18.4 (or earlier versions) from [Github](https://github.com/Kitware/CMake/releases).
|
||||
**Note:** You can also download alternative CMake versions from [Github](https://github.com/Kitware/CMake/releases) if needed.
|
||||
|
||||
## Python 3
|
||||
### Python 3
|
||||
|
||||
Download an install Python 3.6.6 or higher from [here](https://www.python.org/downloads/).
|
||||
Execute the `Update Shell Profile.command` script that is provided with the installer.
|
||||
|
||||
## OSX SDK
|
||||
### MacOS SDK
|
||||
|
||||
You will need version `10.12` of the OSX SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on OSX. You can get that SDK from [here](https://github.com/phracker/MacOSX-SDKs). You must copy it in to your Xcode SDK directory, e.g.
|
||||
You will need version `10.12` of the macOS SDK for building, otherwise you may have crashing or other unintended issues due to the deprecation of OpenGL on macOS. 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.
|
||||
|
||||
```bash
|
||||
cp -rp ~/Downloads/MacOSX10.12.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
```
|
||||
|
||||
## OpenSSL
|
||||
### OpenSSL
|
||||
|
||||
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
|
||||
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR via
|
||||
`export OPENSSL_ROOT_DIR=/usr/local/opt/openssl`
|
||||
or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`
|
||||
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set `OPENSSL_ROOT_DIR` so CMake can find your installations.
|
||||
For OpenSSL installed via homebrew, set `OPENSSL_ROOT_DIR` via `export OPENSSL_ROOT_DIR=/usr/local/opt/openssl` or by appending `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` to `cmake`.
|
||||
|
||||
## Xcode
|
||||
## Generate and Build
|
||||
|
||||
You can choose to use either Unix Makefiles or Xcode.
|
||||
|
||||
### Xcode
|
||||
|
||||
You can ask CMake to generate Xcode project files instead of Unix Makefiles using the `-G Xcode` parameter after CMake. You will need to select the Xcode installation in the terminal first if you have not done so already.
|
||||
|
||||
```bash
|
||||
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
cmake ../ -DCMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOSX_SDK=10.12 ..
|
||||
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 -G Xcode -DOSX_SDK=10.12 ..
|
||||
```
|
||||
|
||||
If `cmake` complains about Python 3 being missing, you may need to update your CMake binary with command `brew upgrade cmake`, or by downloading and running the latest CMake installer, depending on how you originally installed CMake.
|
||||
|
||||
After running CMake, you will have the make files or Xcode project file necessary to build all of the components. Open the hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target drop down), and click Run.
|
||||
After running CMake, you will have the make files or Xcode project file necessary to build all of the components. Open the `vircadia.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
|
||||
### 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.
|
||||
Run CMake.
|
||||
|
||||
```bash
|
||||
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 ..
|
||||
```
|
||||
|
||||
You can append `-j4` to assign more threads to build with. The number indicates the number of threads, e.g. 4.
|
||||
|
||||
To package the installation, you can simply run `make package` afterwards.
|
||||
|
||||
## Notes
|
||||
|
||||
If build is intended for packaging or creation of AppImage, `VIRCADIA_CPU_ARCHITECTURE`
|
||||
CMake variable needs to be set to architecture specific value.
|
||||
It defaults to `-march=native -mtune=native`, which yields builds optimized for particular
|
||||
machine, but builds will not work on machines lacking same CPU instructions.
|
||||
For packaging and AppImage it is recommended to set it to different value, for example `-msse3`.
|
||||
Setting `VIRCADIA_CPU_ARCHITECTURE` to empty string will use default compiler settings and yield
|
||||
maximum compatibility.
|
||||
|
||||
## FAQ
|
||||
|
||||
1. **Problem:** Running the scheme `interface.app` from Xcode causes a crash for Interface related to `libgl`
|
||||
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
|
||||
2. **Solution:** In the Xcode target settings for `libgl`, set the version to `1.0.0`.
|
||||
2. **Problem:** CMake complains about Python 3 being missing.
|
||||
1. **Cause:** CMake might be out of date.
|
||||
2. **Solution:** Try updating your CMake binary with command `brew upgrade cmake`, or by downloading and running a newer CMake installer, depending on how you originally installed CMake. Please keep in mind the recommended CMake versions noted above.
|
||||
|
|
|
@ -74,6 +74,67 @@ if ((NOT "${RELEASE_TYPE}" STREQUAL "PRODUCTION") AND (NOT "${RELEASE_TYPE}" STR
|
|||
set(RELEASE_TYPE "DEV")
|
||||
endif()
|
||||
|
||||
# VIRCADIA_OPTIMIZE
|
||||
# Variable determining vircadia optimization. If not set, it defaults to true.
|
||||
# It's used to determine build flags for main codebase and for VCPKG dependencies.
|
||||
# Should be set to false to get completely unoptimized build for easier line-by-line debugging
|
||||
|
||||
if( NOT WIN32 )
|
||||
if(NOT DEFINED VIRCADIA_OPTIMIZE)
|
||||
message("Enabling code optimization for Vircadia and compiled dependencies")
|
||||
set(VIRCADIA_OPTIMIZE true CACHE BOOL "Enable code optimization for Vircadia and compiled dependencies")
|
||||
endif()
|
||||
|
||||
#compiler needs to be detected before building VCPKG dependencies
|
||||
set(CMAKE_PLATFORM_INFO_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
include(CMakeDetermineCXXCompiler)
|
||||
|
||||
set(VIRCADIA_OPTIMIZE_FLAGS "")
|
||||
|
||||
if(VIRCADIA_OPTIMIZE)
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_env/VIRCADIA_OPTIMIZE.txt" "${VIRCADIA_OPTIMIZE}")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
message("Clang compiler detected, adding -O3 -fPIC -g flags")
|
||||
set(VIRCADIA_OPTIMIZE_FLAGS "-O3 -fPIC -g")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
message("GCC compiler detected, adding -O3 -fPIC -ggdb flags")
|
||||
set(VIRCADIA_OPTIMIZE_FLAGS "-O3 -fPIC -ggdb")
|
||||
else()
|
||||
message("No predefined optimization flags for compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
endif()
|
||||
MESSAGE(STATUS "VIRCADIA_OPTIMIZE: ${VIRCADIA_OPTIMIZE}")
|
||||
|
||||
# VIRCADIA_CPU_ARCHITECTURE
|
||||
# Variable determining CPU architecture for which Vircadia will be built.
|
||||
# If defined, it's appended to CXXFLAGS and CFLAGS for both Vircadia and VCPKG dependencies
|
||||
|
||||
#Assume -march=native for compilers that allow it if architecture is not specified
|
||||
if(NOT DEFINED VIRCADIA_CPU_ARCHITECTURE)
|
||||
if(VIRCADIA_OPTIMIZE AND ( (CMAKE_CXX_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "GNU") ) )
|
||||
message("Optimization is enabled, but architecture is not specified. Assuming native build")
|
||||
set(VIRCADIA_CPU_ARCHITECTURE "-march=native -mtune=native" CACHE STRING "Specify architecture dependent compiler flags here")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED VIRCADIA_CPU_ARCHITECTURE)
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_env/VIRCADIA_CPU_ARCHITECTURE.txt" "${VIRCADIA_CPU_ARCHITECTURE}")
|
||||
set(VIRCADIA_OPTIMIZE_FLAGS "${VIRCADIA_OPTIMIZE_FLAGS} ${VIRCADIA_CPU_ARCHITECTURE}")
|
||||
message("Adding CPU architecture flags: ${VIRCADIA_CPU_ARCHITECTURE}")
|
||||
MESSAGE(STATUS "VIRCADIA_CPU_ARCHITECTURE: ${VIRCADIA_CPU_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VIRCADIA_OPTIMIZE_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${VIRCADIA_OPTIMIZE_FLAGS}")
|
||||
|
||||
set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} ${VIRCADIA_OPTIMIZE_FLAGS}")
|
||||
set(ENV{CFLAGS} "$ENV{CFLAGS} ${VIRCADIA_OPTIMIZE_FLAGS}")
|
||||
message($ENV{CXXFLAGS})
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
if (HIFI_ANDROID)
|
||||
execute_process(
|
||||
COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --release-type ${RELEASE_TYPE} --android ${HIFI_ANDROID_APP} --build-root ${CMAKE_BINARY_DIR}
|
||||
|
|
|
@ -948,7 +948,7 @@ In an international environment English is the preferred language.
|
|||
|
||||
#### [4.3.2] Use // for all comments, including multi-line comments.
|
||||
|
||||
An exception to this rule applies for jsdoc or Doxygen comments.
|
||||
An exception to this rule applies to JSDoc and Doxygen comments.
|
||||
|
||||
```cpp
|
||||
// Comment spanning
|
||||
|
@ -1008,3 +1008,11 @@ These types of comments are explicitly not allowed. If you need to break up sect
|
|||
//--------------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
#### [4.3.6] Doxygen comments should use "///"
|
||||
|
||||
Use the `///` style of [Doxygen](https://www.doxygen.nl/index.html) comments when documenting public interfaces.
|
||||
|
||||
Some editors can automatically create a Doxygen documentation stub if you type `///` in the line above the item to be
|
||||
documented.
|
||||
|
||||
**Visual Studio:** To configure Visual Studio's Doxygen commenting behavior, search for "Doxygen" in Tools > Options.
|
||||
|
|
|
@ -187,13 +187,13 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS
|
|||
```bash
|
||||
cd ../Vircadia/source/pkg-scripts/
|
||||
```
|
||||
17. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
17. Generate the .deb package. Set `DEBVERSION` to the same version you entered for the `Release number` on Vircadia Builder. Set `DEBEMAIL` and `DEBFULLNAME` to your own information to be packaged with the release. *The version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
```bash
|
||||
DEBVERSION="2021.1.0" DEBEMAIL="your-email@somewhere.com" DEBFULLNAME="Your Full Name" ./make-deb-server
|
||||
```
|
||||
18. If successful, the generated .deb package will be in the `pkg-scripts` folder.
|
||||
|
||||
##### Amazon Linux 2 | .rpm
|
||||
##### Amazon Linux 2 | .rpm (Deprecated)
|
||||
|
||||
1. Ensure you are using an Amazon Linux 2 system. You will need many CPU cores to complete this process within a reasonable time. As an alternative to AWS EC2, you may use a [virtual machine](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/amazon-linux-2-virtual-machine.html). Here are the recommended specs:
|
||||
```text
|
||||
|
@ -266,7 +266,7 @@ For code signing to work, you will need to set the `HF_PFX_FILE` and `HF_PFX_PAS
|
|||
```bash
|
||||
cd ../Vircadia/source/pkg-scripts/
|
||||
```
|
||||
18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *Advanced users: the version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
18. Generate the .rpm package. Set `RPMVERSION` to the same version you entered for the `Release number` on Vircadia Builder. *The version cannot begin with a letter and cannot include underscores or dashes in it.*
|
||||
```bash
|
||||
RPMVERSION="2021.1.0" ./make-rpm-server
|
||||
```
|
||||
|
|
25
README.md
25
README.md
|
@ -1,8 +1,17 @@
|
|||
<p align="center"><a href="https://vircadia.com/"><img src="interface/resources/images/vircadia-banner.svg" alt="Vircadia - Codename Athena" width="350"/></a></p>
|
||||
<h3 align="center"><a href="https://vircadia.com/">Website</a> | <a href="https://discordapp.com/invite/Pvx2vke">Discord</a> | <a href="https://vircadia.com/download-vircadia/">Download</a></h3>
|
||||
<p align="center"><a href="CONTRIBUTING.md"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/vircadia/vircadia"></a> <a href="https://github.com/vircadia/vircadia/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/vircadia/vircadia"></a> <a href="https://github.com/vircadia/vircadia/network"><img alt="GitHub forks" src="https://img.shields.io/github/forks/vircadia/vircadia"></a> <a href="https://www.apache.org/licenses/LICENSE-2.0"><img alt="Apache 2.0" src="https://img.shields.io/badge/license-Apache--2.0-%230A7BBB?style=flat"></a> <a href="https://discordapp.com/invite/Pvx2vke"><img alt="Discord" src="https://img.shields.io/discord/564926326025224212?style=flat"></a></p>
|
||||
<p align="center">
|
||||
<a href="https://vircadia.com/contribute"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/vircadia/vircadia"></a>
|
||||
<a href="https://github.com/sponsors/digisomni"><img alt="GitHub sponsors" src="https://img.shields.io/github/sponsors/digisomni?style=flat&label=github%20sponsors"></a>
|
||||
<a href="https://github.com/vircadia/vircadia/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/vircadia/vircadia"></a>
|
||||
<a href="https://github.com/vircadia/vircadia/network"><img alt="GitHub forks" src="https://img.shields.io/github/forks/vircadia/vircadia"></a>
|
||||
<a href="https://www.apache.org/licenses/LICENSE-2.0"><img alt="Apache 2.0" src="https://img.shields.io/badge/license-Apache--2.0-%230A7BBB?style=flat"></a>
|
||||
<a href="https://discordapp.com/invite/Pvx2vke"><img alt="Discord" src="https://img.shields.io/discord/564926326025224212?style=flat"></a>
|
||||
</p>
|
||||
<h3 align="center">Build Status</h3>
|
||||
<p align="center"><a href="https://github.com/vircadia/vircadia/actions/workflows/master_build.yml"><img alt="Master CI Build" src="https://github.com/vircadia/vircadia/actions/workflows/master_build.yml/badge.svg"></a></p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/vircadia/vircadia/actions/workflows/master_build.yml"><img alt="Master CI Build" src="https://github.com/vircadia/vircadia/actions/workflows/master_build.yml/badge.svg"></a>
|
||||
</p>
|
||||
|
||||
### What is this?
|
||||
|
||||
|
@ -50,7 +59,7 @@ Vircadia™ is a 3D social software project seeking to incrementally bring about
|
|||
|
||||
### Boot to Metaverse: [The Goal](https://vircadia.com/vision/)
|
||||
|
||||
Having a place to experience adventure, a place to relax with calm breath, that's a world to live in. An engine to support infinite combinations and possibilities of worlds without censorship and interruption, that's a metaverse. Finding a way to make infinite realities our reality is the dream.
|
||||
Having a place to experience adventure, a place to relax with calm breath, that's a world to live in. An engine to support infinite combinations and possibilities of worlds without censorship and interruption, that's metaverse. Finding a way to make infinite realities our reality is the dream.
|
||||
|
||||
### Boot to Metaverse: The Technicals
|
||||
|
||||
|
@ -64,14 +73,16 @@ Vircadia consists of many projects and codebases with its unifying structure's g
|
|||
- [The Metaverse Server Dashboard (Codename Iamus)](https://github.com/vircadia/project-iamus-dashboard/)
|
||||
- [The Launcher (Codename Pantheon)](https://github.com/vircadia/pantheon-launcher/) - Currently Windows only.
|
||||
|
||||
#### Child Projects
|
||||
#### Tools
|
||||
- [Vircadia Builder for Linux](https://github.com/vircadia/vircadia-builder/)
|
||||
|
||||
#### Documentation
|
||||
- [User Documentation](https://github.com/vircadia/vircadia-docs-sphinx/)
|
||||
- [Developer Documentation](https://github.com/vircadia/vircadia-dev-docs/)
|
||||
|
||||
### Contribution
|
||||
|
||||
There are many contributors to Vircadia. Code writers, reviewers, testers, documentation writers, modelers, and general supporters of the project are all integral to its development and success towards its goals. Find out how you can [contribute](CONTRIBUTING.md)!
|
||||
There are many contributors to Vircadia. Code writers, reviewers, testers, documentation writers, modelers, and general supporters of the project are all integral to its development and success towards its goals. Find out how you can [contribute](https://vircadia.com/contribute)!
|
||||
|
||||
### Support
|
||||
|
||||
|
@ -83,6 +94,6 @@ Keep in mind that Vircadia consists of multiple smaller projects that might have
|
|||
|
||||
#### Supporters of the Vircadia Project
|
||||
|
||||
| [Karol Suprynowicz - 74hc595](https://github.com/ksuprynowicz) |
|
||||
| [ksuprynowicz (74hc595)](https://github.com/ksuprynowicz) |
|
||||
| --- |
|
||||
| <p align="center">[<img src="https://vircadia.com/wp-content/uploads/2021/07/74hc595_profile_2-1.png" width="80" alt="ksuprynowicz" />](https://github.com/ksuprynowicz)</p>
|
||||
| <p align="center">[<img src="https://vircadia.com/wp-content/uploads/2021/07/74hc595_profile_2-1.png" width="80" alt="ksuprynowicz" />](https://github.com/ksuprynowicz)</p>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
#include <QSharedMemory>
|
||||
#include <QThread>
|
||||
|
@ -84,7 +85,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
_assignmentServerHostname = assignmentServerHostname;
|
||||
}
|
||||
|
||||
_assignmentServerSocket = SockAddr(_assignmentServerHostname, assignmentServerPort, true);
|
||||
_assignmentServerSocket = SockAddr(SocketType::UDP, _assignmentServerHostname, assignmentServerPort, true);
|
||||
if (_assignmentServerSocket.isNull()) {
|
||||
qCCritical(assignment_client) << "PAGE: Couldn't resolve domain server address" << _assignmentServerHostname;
|
||||
}
|
||||
|
@ -119,7 +120,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
|
||||
// did we get an assignment-client monitor port?
|
||||
if (assignmentMonitorPort > 0) {
|
||||
_assignmentClientMonitorSocket = SockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
|
||||
_assignmentClientMonitorSocket = SockAddr(SocketType::UDP, DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME,
|
||||
assignmentMonitorPort);
|
||||
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
|
||||
|
||||
qCDebug(assignment_client) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
|
||||
|
@ -132,6 +134,18 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
|
||||
packetReceiver.registerListener(PacketType::StopNode,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleStopNodePacket));
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
auto webrtcSocket = nodeList->getWebRTCSocket();
|
||||
|
||||
// Route inbound WebRTC signaling messages from the Domain Server.
|
||||
packetReceiver.registerListener(PacketType::WebRTCSignaling,
|
||||
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleWebRTCSignalingPacket));
|
||||
connect(this, &AssignmentClient::webrtcSignalingMessageFromUserClient, webrtcSocket, &WebRTCSocket::onSignalingMessage);
|
||||
|
||||
// Route outbound WebRTC signaling messages via the Domain Server to the user client.
|
||||
connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage, this, &AssignmentClient::sendSignalingMessageToUserClient);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AssignmentClient::stopAssignmentClient() {
|
||||
|
@ -340,3 +354,45 @@ void AssignmentClient::assignmentCompleted() {
|
|||
|
||||
_isAssigned = false;
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
void AssignmentClient::handleWebRTCSignalingPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
auto messageString = message->readString();
|
||||
auto json = QJsonDocument::fromJson(messageString.toUtf8()).object();
|
||||
if (json.keys().contains("echo")) {
|
||||
// Echo message back to sender.
|
||||
|
||||
if (!json.keys().contains("to") || !json.keys().contains("from")) {
|
||||
qCDebug(assignment_client) << "Invalid WebRTC signaling echo message received.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap to/from.
|
||||
auto to = json.value("to");
|
||||
json.insert("to", json.value("from"));
|
||||
json.insert("from", to);
|
||||
|
||||
// Send back to sender via the Domain Server.
|
||||
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
|
||||
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto domainServerAddress = nodeList->getDomainHandler().getSockAddr();
|
||||
nodeList->sendPacketList(std::move(packetList), domainServerAddress);
|
||||
|
||||
} else {
|
||||
// WebRTC signaling message.
|
||||
emit webrtcSignalingMessageFromUserClient(json);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends a signaling message from the assignment client to the user client via the Domain Server.
|
||||
void AssignmentClient::sendSignalingMessageToUserClient(const QJsonObject& json) {
|
||||
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
|
||||
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto domainServerAddress = nodeList->getDomainHandler().getSockAddr();
|
||||
nodeList->sendPacketList(std::move(packetList), domainServerAddress);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#include "ThreadedAssignment.h"
|
||||
|
||||
class QSharedMemory;
|
||||
|
@ -30,19 +32,26 @@ public:
|
|||
bool disableDomainPortAutoDiscovery);
|
||||
~AssignmentClient();
|
||||
|
||||
public slots:
|
||||
void aboutToQuit();
|
||||
|
||||
private slots:
|
||||
void sendAssignmentRequest();
|
||||
void assignmentCompleted();
|
||||
void handleAuthenticationRequest();
|
||||
void sendStatusPacketToACM();
|
||||
void stopAssignmentClient();
|
||||
|
||||
public slots:
|
||||
void aboutToQuit();
|
||||
|
||||
private slots:
|
||||
void handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleStopNodePacket(QSharedPointer<ReceivedMessage> message);
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
void handleWebRTCSignalingPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void sendSignalingMessageToUserClient(const QJsonObject& json);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
void webrtcSignalingMessageFromUserClient(const QJsonObject& json);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void setUpStatusToMonitor();
|
||||
|
|
|
@ -110,3 +110,5 @@ if (APPLE)
|
|||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
message("CXXFLAGS: ${CXXFLAGS}")
|
||||
|
|
|
@ -12,7 +12,7 @@ macro(TARGET_OPENSSL)
|
|||
set(OPENSSL_LIBRARIES "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a;${OPENSSL_INSTALL_DIR}/lib/libssl.a" CACHE STRING INTERNAL)
|
||||
else()
|
||||
# using VCPKG for OpenSSL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(OpenSSL 1.1.0 REQUIRED)
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
include(vcpkg_common_functions)
|
||||
|
||||
|
||||
if (VCPKG_LIBRARY_LINKAGE STREQUAL dynamic)
|
||||
message(WARNING "Dynamic not supported, building static")
|
||||
set(VCPKG_LIBRARY_LINKAGE static)
|
||||
|
@ -30,6 +31,32 @@ vcpkg_from_github(
|
|||
PATCHES "bullet-git-fix-build-clang-8.patch"
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(VIRCADIA_BULLET_OPTIONS "")
|
||||
else()
|
||||
if(EXISTS "${VCPKG_ROOT_DIR}/_env/VIRCADIA_OPTIMIZE.txt")
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/VIRCADIA_OPTIMIZE.txt" VIRCADIA_OPTIMIZE)
|
||||
endif()
|
||||
if(EXISTS "${VCPKG_ROOT_DIR}/_env/VIRCADIA_CPU_ARCHITECTURE.txt")
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/VIRCADIA_CPU_ARCHITECTURE.txt" VIRCADIA_CPU_ARCHITECTURE)
|
||||
endif()
|
||||
|
||||
|
||||
if(VIRCADIA_OPTIMIZE)
|
||||
set(VIRCADIA_BULLET_OPTIONS "-DCMAKE_BUILD_TYPE=Release")
|
||||
else()
|
||||
set(VIRCADIA_BULLET_OPTIONS "-DCMAKE_BUILD_TYPE=RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
set(VIRCADIA_BULLET_OPTIONS "${VIRCADIA_BULLET_OPTIONS}")
|
||||
|
||||
if(DEFINED VIRCADIA_CPU_ARCHITECTURE)
|
||||
set(VIRCADIA_BULLET_OPTIONS "${VIRCADIA_BULLET_OPTIONS} -DCMAKE_CXX_FLAGS=\"${VIRCADIA_CPU_ARCHITECTURE}\" -DCMAKE_C_FLAGS=\"${VIRCADIA_CPU_ARCHITECTURE}\" ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message("Optimization options for Bullet: ${VIRCADIA_BULLET_OPTIONS}")
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
SOURCE_PATH ${SOURCE_PATH}
|
||||
OPTIONS
|
||||
|
@ -46,6 +73,7 @@ vcpkg_configure_cmake(
|
|||
-DBUILD_UNIT_TESTS=OFF
|
||||
-DBUILD_SHARED_LIBS=ON
|
||||
-DINSTALL_LIBS=ON
|
||||
${VIRCADIA_BULLET_OPTIONS}
|
||||
)
|
||||
|
||||
vcpkg_install_cmake()
|
||||
|
@ -58,4 +86,4 @@ file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/include/bullet/BulletInverseDynamics
|
|||
vcpkg_copy_pdbs()
|
||||
|
||||
# Handle copyright
|
||||
file(INSTALL ${SOURCE_PATH}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/bullet3 RENAME copyright)
|
||||
file(INSTALL ${SOURCE_PATH}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/bullet3 RENAME copyright)
|
||||
|
|
|
@ -12,7 +12,37 @@ vcpkg_from_github(
|
|||
HEAD_REF
|
||||
master)
|
||||
|
||||
vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH} PREFER_NINJA)
|
||||
if(WIN32)
|
||||
set(VIRCADIA_OPUS_OPTIONS "")
|
||||
else()
|
||||
if(EXISTS "${VCPKG_ROOT_DIR}/_env/VIRCADIA_OPTIMIZE.txt")
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/VIRCADIA_OPTIMIZE.txt" VIRCADIA_OPTIMIZE)
|
||||
endif()
|
||||
if(EXISTS "${VCPKG_ROOT_DIR}/_env/VIRCADIA_CPU_ARCHITECTURE.txt")
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/VIRCADIA_CPU_ARCHITECTURE.txt" VIRCADIA_CPU_ARCHITECTURE)
|
||||
endif()
|
||||
|
||||
if(VIRCADIA_OPTIMIZE)
|
||||
set(VIRCADIA_OPUS_OPTIONS "-DCMAKE_BUILD_TYPE=Release")
|
||||
else()
|
||||
set(VIRCADIA_OPUS_OPTIONS "-DCMAKE_BUILD_TYPE=RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
set(VIRCADIA_OPUS_OPTIONS "${VIRCADIA_OPUS_OPTIONS}")
|
||||
|
||||
if(DEFINED VIRCADIA_CPU_ARCHITECTURE)
|
||||
set(VIRCADIA_OPUS_OPTIONS "${VIRCADIA_OPUS_OPTIONS} -DCMAKE_CXX_FLAGS=\"${VIRCADIA_CPU_ARCHITECTURE}\" -DCMAKE_C_FLAGS=\"${VIRCADIA_CPU_ARCHITECTURE}\" ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message("Optimization options for Opus: ${VIRCADIA_OPUS_OPTIONS}")
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
SOURCE_PATH ${SOURCE_PATH}
|
||||
PREFER_NINJA
|
||||
OPTIONS ${VIRCADIA_OPUS_OPTIONS}
|
||||
)
|
||||
|
||||
vcpkg_install_cmake()
|
||||
vcpkg_fixup_cmake_targets(CONFIG_PATH lib/cmake/Opus)
|
||||
vcpkg_copy_pdbs()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Source: webrtc
|
||||
Version: 20190626
|
||||
Version: 20210105
|
||||
Description: WebRTC
|
||||
|
|
215
cmake/ports/webrtc/README.md
Normal file
215
cmake/ports/webrtc/README.md
Normal file
|
@ -0,0 +1,215 @@
|
|||
# WebRTC
|
||||
|
||||
WebRTC Information:
|
||||
- https://webrtc.org/
|
||||
- https://webrtc.googlesource.com/src
|
||||
- https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/index.md
|
||||
- https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/development/prerequisite-sw/index.md
|
||||
- https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/development/index.md
|
||||
- https://www.chromium.org/developers/calendar
|
||||
- https://github.com/microsoft/winrtc
|
||||
- https://docs.microsoft.com/en-us/winrtc/getting-started
|
||||
- https://groups.google.com/g/discuss-webrtc \
|
||||
See "PSA" posts for release information.
|
||||
- https://bugs.chromium.org/p/webrtc/issues/list
|
||||
- https://stackoverflow.com/questions/27809193/webrtc-not-building-for-windows
|
||||
- https://github.com/aisouard/libwebrtc/issues/57
|
||||
|
||||
## Windows - M84
|
||||
|
||||
WebRTC's M84 release is currently used because it corresponded to Microsoft's latest WinRTC release at the time of development,
|
||||
and WinRTC is a source of potentially useful patches.
|
||||
|
||||
The following notes document how the M84-based Windows VCPKG was created, using Visual Studio 2019.
|
||||
|
||||
### Set Up depot_tools
|
||||
|
||||
Install Google's depot_tools.
|
||||
- Download depot_tools.zip.
|
||||
- Extract somewhere.
|
||||
- Add the extracted directory to the start of the system `PATH` environment variable.
|
||||
|
||||
Configure depot_tools.
|
||||
- Set an environment variable `DEPOT_TOOLS_WIN_TOOLCHAIN=0`
|
||||
- Set an environment variable `GYP_MSVS_VERSION=2019`
|
||||
|
||||
Initialize depot_tools.
|
||||
- VS2019 developer command prompt in the directory where the source tree will be created.
|
||||
- `gclient`
|
||||
|
||||
### Get the Code
|
||||
|
||||
Fetch the code into a *\src* subdirectory. This may take some time!
|
||||
- `fetch --nohooks webrtc`
|
||||
|
||||
Switch to the M84 branch.
|
||||
- `cd src`
|
||||
- `git checkout branch-heads/4147`
|
||||
|
||||
Fetch all the subrepositories.
|
||||
- `gclient sync -D -r branch-heads/4147`
|
||||
|
||||
### Patch the Code
|
||||
|
||||
#### Modify compiler switches
|
||||
- Edit *build\config\win\BUILD.gn*:
|
||||
- Change all `/MT` to `/MD`, and `/MTd` to `/MDd`.
|
||||
- Change all `cflags = [ "/MDd" ]` to `[ "/MDd", "-D_ITERATOR_DEBUG_LEVEL=2", "-D_HAS_ITERATOR_DEBUGGING=1" ]`.
|
||||
- Edit *build\config\compiler\BUILD.gn*:\
|
||||
Change:
|
||||
```
|
||||
if (is_win) {
|
||||
if (is_clang) {
|
||||
cflags = [ "/Z7" ] # Debug information in the .obj files.
|
||||
} else {
|
||||
cflags = [ "/Zi" ] # Produce PDB file, no edit and continue.
|
||||
}
|
||||
```
|
||||
to:
|
||||
```
|
||||
if (is_win) {
|
||||
if (is_clang) {
|
||||
cflags = [ "/Z7", "/std:c++17", "/Zc:__cplusplus" ] # Debug information in the .obj files.
|
||||
} else {
|
||||
cflags = [ "/Zi", "/std:c++17", "/Zc:__cplusplus" ] # Produce PDB file, no edit and continue.
|
||||
}
|
||||
```
|
||||
|
||||
#### H265 Codec Fixes
|
||||
https://bugs.webrtc.org/9213#c13
|
||||
- Edit the following files:
|
||||
- *modules\video_coding\codecs\h264\h264_color_space.h*
|
||||
- *modules\video_coding\codecs\h264\h264_decoder_impl.h*
|
||||
- *modules\video_coding\codecs\h264\h264_encoder_impl.h*
|
||||
In each, comment out the following lines:
|
||||
```
|
||||
#if defined(WEBRTC_WIN) && !defined(__clang__)
|
||||
#error "See: bugs.webrtc.org/9213#c13."
|
||||
#endif
|
||||
```
|
||||
- Edit *third_party\ffmpeg\libavcodec\fft_template.c*:\
|
||||
Comment out all of `ff_fft_init` except the fail clause at the end.
|
||||
- Edit *third_party\ffmpeg\libavcodec\pcm.c*:\
|
||||
Comment out last line, containing `PCM Archimedes VIDC`.
|
||||
- Edit *third_party\ffmpeg\libavutil\x86\imgutils_init.c*:\
|
||||
Add the following method to the end of the file:
|
||||
```
|
||||
void avpriv_emms_asm(void) {} // Fix missing symbol in FFMPEG.
|
||||
```
|
||||
|
||||
#### Exclude BoringSSL
|
||||
A separate OpenSSL VCPKG is used for building Vircadia.
|
||||
The following patches are needed even though SSL is excluded in the `gn gen` build commands.
|
||||
- Rename *third_party\boringssl* to *third_party\boringssl-NO*.
|
||||
- Edit *third_party\libsrtp\BUILD.gn:\
|
||||
Change:
|
||||
```
|
||||
public_deps = [
|
||||
"//third_party/boringssl:boringssl",
|
||||
]
|
||||
```
|
||||
To:
|
||||
```
|
||||
public_deps = [
|
||||
# "//third_party/boringssl:boringssl",
|
||||
]
|
||||
```
|
||||
|
||||
- Edit *third_party\usrsctp\BUILD.gn*:\
|
||||
Change:
|
||||
```
|
||||
deps = [ "//third_party/boringssl" ]
|
||||
```
|
||||
To:
|
||||
```
|
||||
deps = [
|
||||
# "//third_party/boringssl"
|
||||
]
|
||||
```
|
||||
- Edit *base\BUILD.gn*:\
|
||||
In the code under:
|
||||
```
|
||||
# Use the base implementation of hash functions when building for
|
||||
# NaCl. Otherwise, use boringssl.
|
||||
```
|
||||
Change:
|
||||
```
|
||||
if (is_nacl) {
|
||||
```
|
||||
To:
|
||||
```
|
||||
# if (is_nacl) {
|
||||
if (true) {
|
||||
```
|
||||
- Edit *rtc_base\BUILD.gn*:\
|
||||
Change:
|
||||
```
|
||||
if (rtc_build_ssl) {
|
||||
deps += [ "//third_party/boringssl" ]
|
||||
} else {
|
||||
```
|
||||
To:
|
||||
```
|
||||
if (rtc_build_ssl) {
|
||||
# deps += [ "//third_party/boringssl" ]
|
||||
} else {
|
||||
```
|
||||
|
||||
### Set Up OpenSSL
|
||||
|
||||
Do one of the following to provide OpenSSL for building against:
|
||||
a. If you have built Vircadia, find the **HIFI_VCPKG_BASE** subdirectory used in your build and make note of the path to and
|
||||
including the *installed\x64-windows\include* directory (which includes an *openssl* directory).
|
||||
a. Follow https://github.com/vircadia/vcpkg to install *vcpkg* and then *openssl*. Make note of the path to and including the
|
||||
*packages\openssl-windows_x64-windows\include* directory (which includes an *openssl* directory).
|
||||
|
||||
Copy the *\<path\>\openssl* directory to the following locations (i.e., add as *openssl* subdirectories):
|
||||
- *third_party\libsrtp\crypto\include*
|
||||
- *third_party\usrsctp\usrsctplib\usrsctplib*
|
||||
|
||||
Also use the path in the `gn gen` commands, below, making sure to escape `\`s as `\\`s.
|
||||
|
||||
### Build and Package
|
||||
|
||||
Use a VS2019 developer command prompt in the *src* directory.
|
||||
|
||||
Create a release build of the WebRTC library:
|
||||
- `gn gen --ide=vs2019 out\Release --filters=//:webrtc "--args=is_debug=false is_clang=false use_custom_libcxx=false libcxx_is_shared=true symbol_level=2 use_lld=false rtc_include_tests=false rtc_build_tools=false rtc_build_examples=false proprietary_codecs=true rtc_use_h264=true enable_libaom=false rtc_enable_protobuf=false rtc_build_ssl=false rtc_ssl_root=\"<path>\""`
|
||||
- `ninja -C out\Release`
|
||||
|
||||
Create a debug build of the WebRTC library:
|
||||
- `gn gen --ide=vs2019 out\Debug --filters=//:webrtc "--args=is_debug=true is_clang=false use_custom_libcxx=false libcxx_is_shared=true enable_iterator_debugging=true use_lld=false rtc_include_tests=false rtc_build_tools=false rtc_build_examples=false proprietary_codecs=true rtc_use_h264=true enable_libaom=false rtc_enable_protobuf=false rtc_build_ssl=false rtc_ssl_root=\"<path>\""`
|
||||
- `ninja -C out\Debug`
|
||||
|
||||
Create VCPKG file:
|
||||
- Assemble files in VCPKG directory structure: Use the *copy-VCPKG-files-win.cmd* batch file per instructions in it.
|
||||
`cd ..`\
|
||||
`copy-VCPKG-files-win`
|
||||
- Zip up the VCPKG *webrtc* directory created by the batch file.
|
||||
`cd vcpkg`\
|
||||
`7z a -tzip webrtc-m84-yyyymmdd-windows.zip webrtc`
|
||||
- Calculate the SHA512 of the zip file. E.g., using a Windows PowerShell command window:\
|
||||
`Get-FileHash <filename> -Algorithm SHA512 | Format-List`
|
||||
- Convert the SHA512 to lower case. E.g., using Microsoft Word, select the SHA512 text and use Shift-F3 to change the case.
|
||||
- Host the zip file on the Web.
|
||||
- Update *CONTROL* and *portfile.cmake* with version number (revision date), zip file Web URL, and SHA512.
|
||||
|
||||
### Tidying up
|
||||
|
||||
Disable the depot_tools:
|
||||
- Rename the *depot_tools* directory so that these tools don't interfere with the rest of your development environment for
|
||||
other work.
|
||||
|
||||
|
||||
## Linux - M81
|
||||
|
||||
The original, High Fidelity-provided WebRTC VCPKG library is used for AEC (audio echo cancellation) only.
|
||||
|
||||
**TODO:** Update to M84 and include WebRTC components per Windows WebRTC.
|
||||
|
||||
|
||||
## MacOS - M78
|
||||
|
||||
The original, High Fidelity-provided WebRTC VCPKG library is used for AEC (audio echo cancellation) only.
|
||||
|
||||
**TODO:** Update to M84 and include WebRTC components per Windows WebRTC.
|
36
cmake/ports/webrtc/copy-VCPKG-file-win.cmd
Normal file
36
cmake/ports/webrtc/copy-VCPKG-file-win.cmd
Normal file
|
@ -0,0 +1,36 @@
|
|||
rem Copy this file to a directory above the WebRTC \src directory and run it from there in a command window.
|
||||
set WEBRTC_SRC_DIR=src
|
||||
set RELEASE_LIB_DIR=%WEBRTC_SRC_DIR%\out\Release\obj
|
||||
set DEBUG_LIB_DIR=%WEBRTC_SRC_DIR%\out\Debug\obj
|
||||
set VCPKG_TGT_DIR=vcpkg
|
||||
|
||||
if exist %VCPKG_TGT_DIR% rd /s /q %VCPKG_TGT_DIR%
|
||||
mkdir %VCPKG_TGT_DIR%
|
||||
|
||||
rem License and .lib files
|
||||
mkdir %VCPKG_TGT_DIR%\webrtc\share\webrtc\
|
||||
copy %WEBRTC_SRC_DIR%\LICENSE %VCPKG_TGT_DIR%\webrtc\share\webrtc\copyright
|
||||
xcopy /v %RELEASE_LIB_DIR%\webrtc.lib %VCPKG_TGT_DIR%\webrtc\lib\
|
||||
xcopy /v %DEBUG_LIB_DIR%\webrtc.lib %VCPKG_TGT_DIR%\webrtc\debug\lib\
|
||||
|
||||
rem Header files
|
||||
mkdir %VCPKG_TGT_DIR%\webrtc\include\webrtc\
|
||||
copy %WEBRTC_SRC_DIR%\common_types.h %VCPKG_TGT_DIR%\webrtc\include\webrtc
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\api\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\api
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\audio\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\audio
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\base\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\base
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\call\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\call
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\common_audio\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\common_audio
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\common_video\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\common_video
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\logging\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\logging
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\media\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\media
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\modules\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\modules
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\p2p\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\p2p
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\pc\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\pc
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\rtc_base\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\rtc_base
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\rtc_tools\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\rtc_tools
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\stats\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\stats
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\system_wrappers\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\system_wrappers
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\third_party\abseil-cpp\absl\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\absl
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\third_party\libyuv\include\libyuv\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\libyuv
|
||||
xcopy /v /s /i %WEBRTC_SRC_DIR%\video\*.h %VCPKG_TGT_DIR%\webrtc\include\webrtc\video
|
|
@ -1,5 +1,5 @@
|
|||
include(vcpkg_common_functions)
|
||||
set(WEBRTC_VERSION 20190626)
|
||||
set(WEBRTC_VERSION 20210105)
|
||||
set(MASTER_COPY_SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/src)
|
||||
|
||||
file(READ "${VCPKG_ROOT_DIR}/_env/EXTERNAL_BUILD_ASSETS.txt" EXTERNAL_BUILD_ASSETS)
|
||||
|
@ -9,9 +9,9 @@ if (ANDROID)
|
|||
elseif (WIN32)
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/seth/webrtc-20190626-windows.zip"
|
||||
SHA512 c0848eddb1579b3bb0496b8785e24f30470f3c477145035fd729264a326a467b9467ae9f426aa5d72d168ad9e9bf2c279150744832736bdf39064d24b04de1a3
|
||||
FILENAME webrtc-20190626-windows.zip
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/dependencies/vcpkg/webrtc-m84-20210105-windows.zip"
|
||||
SHA512 12847f7e9df2e0539a6b017db88012a8978b1aa37ff2e8dbf019eb7438055395fdda3a74dc669b0a30330973a83bc57e86eca6f59b1c9eff8e2145a7ea4a532a
|
||||
FILENAME webrtc-m84-20210105-windows.zip
|
||||
)
|
||||
elseif (APPLE)
|
||||
vcpkg_download_distfile(
|
||||
|
@ -24,9 +24,9 @@ else ()
|
|||
# else Linux desktop
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/seth/webrtc-20190626-linux.tar.gz"
|
||||
SHA512 07d7776551aa78cb09a3ef088a8dee7762735c168c243053b262083d90a1d258cec66dc386f6903da5c4461921a3c2db157a1ee106a2b47e7756cb424b66cc43
|
||||
FILENAME webrtc-20190626-linux.tar.gz
|
||||
URLS "${EXTERNAL_BUILD_ASSETS}/dependencies/vcpkg/webrtc-m84-gcc-linux.tar.xz"
|
||||
SHA512 f7c5f93566e2e79241cbb9628ab47302dd48739bb6a022c351be75553060fac4221892d094306a572cb3ec94c5031d7e812f07e7b3c0102be8c01b8c231f8ea0
|
||||
FILENAME webrtc-m84-gcc-linux.tar.xz
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 2.5,
|
||||
"version": 2.6,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -73,6 +73,28 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "webrtc",
|
||||
"label": "Networking / WebRTC",
|
||||
"settings": [
|
||||
{
|
||||
"name": "enable_webrtc",
|
||||
"label": "Enable WebRTC Client Connections",
|
||||
"help": "Allow web clients to connect over WebRTC data channels.",
|
||||
"type": "checkbox",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "enable_webrtc_websocket_ssl",
|
||||
"label": "Enable WebRTC WebSocket SSL",
|
||||
"help": "Use secure WebSocket (wss:// protocol) for WebRTC signaling channel. If \"on\", the key, cert, and CA files are expected to be in the local Vircadia app directory, in a /domain-server/ subdirectory with filenames vircadia-cert.key, vircadia-cert.crt, and vircadia-crt-ca.crt.",
|
||||
"type": "checkbox",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "authentication",
|
||||
"label": "Networking / WordPress OAuth2",
|
||||
|
|
|
@ -194,7 +194,8 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT,
|
||||
QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
|
@ -270,12 +271,32 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
_settingsManager.apiRefreshGroupInformation();
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const QString WEBRTC_ENABLE = "webrtc.enable_webrtc";
|
||||
bool isWebRTCEnabled = _settingsManager.valueForKeyPath(WEBRTC_ENABLE).toBool();
|
||||
qCDebug(domain_server) << "WebRTC enabled:" << isWebRTCEnabled;
|
||||
// The domain server's WebRTC signaling server is used by the domain server and the assignment clients, so disabling it
|
||||
// disables WebRTC for the server as a whole.
|
||||
if (isWebRTCEnabled) {
|
||||
const QString WEBRTC_WSS_ENABLE = "webrtc.enable_webrtc_websocket_ssl";
|
||||
bool isWebRTCEnabled = _settingsManager.valueForKeyPath(WEBRTC_WSS_ENABLE).toBool();
|
||||
qCDebug(domain_server) << "WebRTC WSS enabled:" << isWebRTCEnabled;
|
||||
_webrtcSignalingServer.reset(new WebRTCSignalingServer(this, isWebRTCEnabled));
|
||||
}
|
||||
#endif
|
||||
|
||||
setupNodeListAndAssignments();
|
||||
|
||||
updateReplicatedNodes();
|
||||
updateDownstreamNodes();
|
||||
updateUpstreamNodes();
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
if (isWebRTCEnabled) {
|
||||
setUpWebRTCSignalingServer();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_type != NonMetaverse) {
|
||||
// if we have a metaverse domain, we'll use an access token for API calls
|
||||
resetAccountManagerAccessToken();
|
||||
|
@ -762,7 +783,8 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
|
||||
|
||||
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
|
||||
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getSocketLocalPort());
|
||||
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this,
|
||||
nodeList->getSocketLocalPort(SocketType::UDP));
|
||||
|
||||
// store our local http ports in shared memory
|
||||
quint16 localHttpPort = DOMAIN_SERVER_HTTP_PORT;
|
||||
|
@ -873,6 +895,73 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
// Sets up the WebRTC signaling server that's hosted by the domain server.
|
||||
void DomainServer::setUpWebRTCSignalingServer() {
|
||||
// Bind the WebRTC signaling server's WebSocket to its port.
|
||||
bool isBound = _webrtcSignalingServer->bind(QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT);
|
||||
if (!isBound) {
|
||||
qWarning() << "WebRTC signaling server not bound to port. WebRTC connections are not supported.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// Route inbound WebRTC signaling messages received from user clients.
|
||||
connect(_webrtcSignalingServer.get(), &WebRTCSignalingServer::messageReceived,
|
||||
this, &DomainServer::routeWebRTCSignalingMessage);
|
||||
|
||||
// Route domain server signaling messages.
|
||||
auto webrtcSocket = limitedNodeList->getWebRTCSocket();
|
||||
connect(this, &DomainServer::webrtcSignalingMessageForDomainServer, webrtcSocket, &WebRTCSocket::onSignalingMessage);
|
||||
connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage,
|
||||
_webrtcSignalingServer.get(), &WebRTCSignalingServer::sendMessage);
|
||||
|
||||
// Forward signaling messages received from assignment clients to user client.
|
||||
PacketReceiver& packetReceiver = limitedNodeList->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::WebRTCSignaling,
|
||||
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this,
|
||||
&DomainServer::forwardAssignmentClientSignalingMessageToUserClient));
|
||||
connect(this, &DomainServer::webrtcSignalingMessageForUserClient,
|
||||
_webrtcSignalingServer.get(), &WebRTCSignalingServer::sendMessage);
|
||||
}
|
||||
|
||||
// Routes an inbound WebRTC signaling message received from a client app to the appropriate recipient.
|
||||
void DomainServer::routeWebRTCSignalingMessage(const QJsonObject& json) {
|
||||
if (json.value("to").toString() == NodeType::DomainServer) {
|
||||
emit webrtcSignalingMessageForDomainServer(json);
|
||||
} else {
|
||||
sendWebRTCSignalingMessageToAssignmentClient(json);
|
||||
}
|
||||
}
|
||||
|
||||
// Sends a WebRTC signaling message to the target AC contained in the message.
|
||||
void DomainServer::sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json) {
|
||||
NodeType_t destinationNodeType = NodeType::fromChar(json.value("to").toString().at(0));
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto destinationNode = limitedNodeList->soloNodeOfType(destinationNodeType);
|
||||
if (!destinationNode) {
|
||||
qWarning() << NodeType::getNodeTypeName(destinationNodeType) << "not found for WebRTC signaling message.";
|
||||
return;
|
||||
}
|
||||
// Use an NLPacketList because the signaling message is not necessarily small.
|
||||
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
|
||||
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
limitedNodeList->sendPacketList(std::move(packetList), *destinationNode);
|
||||
}
|
||||
|
||||
// Forwards a WebRTC signaling message received from an assignment client to the relevant user client.
|
||||
void DomainServer::forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer<ReceivedMessage> message) {
|
||||
auto messageString = message->readString();
|
||||
auto json = QJsonDocument::fromJson(messageString.toUtf8()).object();
|
||||
emit webrtcSignalingMessageForUserClient(json);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
bool DomainServer::resetAccountManagerAccessToken() {
|
||||
if (!_oauthProviderURL.isEmpty()) {
|
||||
// check for an access-token in our settings, can optionally be overidden by env value
|
||||
|
@ -3071,6 +3160,7 @@ ReplicationServerInfo serverInformationFromSettings(QVariantMap serverMap, Repli
|
|||
|
||||
// read the address and port and construct a SockAddr from them
|
||||
serverInfo.sockAddr = {
|
||||
SocketType::UDP,
|
||||
serverMap[REPLICATION_SERVER_ADDRESS].toString(),
|
||||
(quint16) serverMap[REPLICATION_SERVER_PORT].toString().toInt()
|
||||
};
|
||||
|
@ -3651,7 +3741,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
indexToTry = distribution(generator);
|
||||
}
|
||||
|
||||
_iceServerSocket = SockAddr { candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT };
|
||||
_iceServerSocket = SockAddr { SocketType::UDP, candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT };
|
||||
qCInfo(domain_server_ice) << "Set candidate ice-server socket to" << _iceServerSocket;
|
||||
|
||||
// clear our number of hearbeat denials, this should be re-set on ice-server change
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <Assignment.h>
|
||||
#include <HTTPSConnection.h>
|
||||
#include <LimitedNodeList.h>
|
||||
#include <shared/WebRTC.h>
|
||||
#include <webrtc/WebRTCSignalingServer.h>
|
||||
|
||||
#include "AssetsBackupHandler.h"
|
||||
#include "DomainGatekeeper.h"
|
||||
|
@ -141,12 +143,17 @@ private slots:
|
|||
void updateReplicatedNodes();
|
||||
void updateDownstreamNodes();
|
||||
void updateUpstreamNodes();
|
||||
|
||||
void initializeExporter();
|
||||
void initializeMetadataExporter();
|
||||
|
||||
void tokenGrantFinished();
|
||||
void profileRequestFinished();
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
void forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer<ReceivedMessage> message);
|
||||
#endif
|
||||
|
||||
void aboutToQuit();
|
||||
|
||||
signals:
|
||||
|
@ -154,6 +161,12 @@ signals:
|
|||
void userConnected();
|
||||
void userDisconnected();
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
void webrtcSignalingMessageForDomainServer(const QJsonObject& json);
|
||||
void webrtcSignalingMessageForUserClient(const QJsonObject& json);
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
QUuid getID();
|
||||
|
||||
|
@ -235,6 +248,12 @@ private:
|
|||
std::initializer_list<QString> optionalData = { },
|
||||
bool requireAccessToken = true);
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
void setUpWebRTCSignalingServer();
|
||||
void routeWebRTCSignalingMessage(const QJsonObject& json);
|
||||
void sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json);
|
||||
#endif
|
||||
|
||||
QString operationToString(const QNetworkAccessManager::Operation &op);
|
||||
|
||||
SubnetList _acSubnetWhitelist;
|
||||
|
@ -312,6 +331,10 @@ private:
|
|||
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
|
||||
|
||||
QThread _assetClientThread;
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
std::unique_ptr<WebRTCSignalingServer> _webrtcSignalingServer { nullptr };
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -551,6 +551,8 @@ void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilena
|
|||
packPermissions();
|
||||
}
|
||||
|
||||
// No migration needed to version 2.6.
|
||||
|
||||
// write the current description version to our settings
|
||||
*versionVariant = _descriptionVersion;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const SockAddr& senderSockAddr,
|
||||
bool isConnectRequest) {
|
||||
NodeConnectionData newHeader;
|
||||
|
||||
|
||||
if (isConnectRequest) {
|
||||
dataStream >> newHeader.connectUUID;
|
||||
|
||||
|
@ -51,9 +51,29 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
|
|||
|
||||
dataStream >> newHeader.lastPingTimestamp;
|
||||
|
||||
SocketType publicSocketType, localSocketType;
|
||||
dataStream >> newHeader.nodeType
|
||||
>> newHeader.publicSockAddr >> newHeader.localSockAddr
|
||||
>> publicSocketType >> newHeader.publicSockAddr >> localSocketType >> newHeader.localSockAddr
|
||||
>> newHeader.interestList >> newHeader.placeName;
|
||||
newHeader.publicSockAddr.setType(publicSocketType);
|
||||
newHeader.localSockAddr.setType(localSocketType);
|
||||
|
||||
// For WebRTC connections, the user client's signaling channel WebSocket address is used instead of the actual data
|
||||
// channel's address.
|
||||
if (senderSockAddr.getType() == SocketType::WebRTC) {
|
||||
if (newHeader.publicSockAddr.getType() != SocketType::WebRTC
|
||||
|| newHeader.localSockAddr.getType() != SocketType::WebRTC) {
|
||||
qDebug() << "Inconsistent WebRTC socket types!";
|
||||
}
|
||||
|
||||
// We don't know whether it's a public or local connection so set both the same.
|
||||
auto address = senderSockAddr.getAddress();
|
||||
auto port = senderSockAddr.getPort();
|
||||
newHeader.publicSockAddr.setAddress(address);
|
||||
newHeader.publicSockAddr.setPort(port);
|
||||
newHeader.localSockAddr.setAddress(address);
|
||||
newHeader.localSockAddr.setPort(port);
|
||||
}
|
||||
|
||||
newHeader.senderSockAddr = senderSockAddr;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ link_hifi_libraries(embedded-webserver networking shared)
|
|||
package_libraries_for_deployment()
|
||||
|
||||
# find OpenSSL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(OpenSSL 1.1.0 REQUIRED)
|
||||
|
||||
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
|
||||
# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
@ -39,7 +40,7 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
{
|
||||
// start the ice-server socket
|
||||
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
|
||||
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
|
||||
_serverSocket.bind(SocketType::UDP, QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
// set processPacket as the verified packet callback for the udt::Socket
|
||||
_serverSocket.setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
|
||||
|
|
|
@ -52,6 +52,20 @@ Item {
|
|||
Layout.preferredWidth: parent.width
|
||||
spacing: 0
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: performanceLowPower
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
height: 18
|
||||
fontSize: 16
|
||||
leftPadding: 0
|
||||
text: "Low Power"
|
||||
checked: Performance.getPerformancePreset() === PerformanceEnums.LOW_POWER
|
||||
onClicked: {
|
||||
Performance.setPerformancePreset(PerformanceEnums.LOW_POWER);
|
||||
root.refreshAllDropdowns();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: performanceLow
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
|
@ -358,6 +372,68 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 20
|
||||
Layout.preferredWidth: parent.width
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: 35
|
||||
|
||||
HifiStylesUit.RalewayRegular {
|
||||
id: antialiasingHeader
|
||||
text: "Anti-aliasing"
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: 130
|
||||
height: parent.height
|
||||
size: 16
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: antialiasingModel
|
||||
|
||||
// Maintain same order as "AntialiasingConfig::Mode".
|
||||
ListElement {
|
||||
text: "None"
|
||||
}
|
||||
ListElement {
|
||||
text: "TAA"
|
||||
}
|
||||
ListElement {
|
||||
text: "FXAA"
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.ComboBox {
|
||||
id: antialiasingDropdown
|
||||
anchors.left: antialiasingHeader.right
|
||||
anchors.leftMargin: 20
|
||||
anchors.top: parent.top
|
||||
width: 280
|
||||
height: parent.height
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
model: antialiasingModel
|
||||
currentIndex: -1
|
||||
|
||||
function refreshAntialiasingDropdown() {
|
||||
antialiasingDropdown.currentIndex = Render.antialiasingMode;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
antialiasingDropdown.refreshAntialiasingDropdown();
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
Render.antialiasingMode = currentIndex;
|
||||
antialiasingDropdown.displayText = model.get(currentIndex).text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,5 +441,6 @@ Item {
|
|||
worldDetailDropdown.refreshWorldDetailDropdown();
|
||||
renderingEffectsDropdown.refreshRenderingEffectsDropdownDisplay();
|
||||
refreshRateDropdown.refreshRefreshRateDropdownDisplay();
|
||||
antialiasingDropdown.refreshAntialiasingDropdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,6 +174,15 @@ Flickable {
|
|||
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
|
||||
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
|
||||
|
||||
SimplifiedControls.RadioButton {
|
||||
id: performanceLow
|
||||
text: "Low Power Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.LOW_POWER ? " (Recommended)" : "")
|
||||
checked: Performance.getPerformancePreset() === PerformanceEnums.LOW_POWER
|
||||
onClicked: {
|
||||
Performance.setPerformancePreset(PerformanceEnums.LOW_POWER);
|
||||
}
|
||||
}
|
||||
|
||||
SimplifiedControls.RadioButton {
|
||||
id: performanceLow
|
||||
text: "Low Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.LOW ? " (Recommended)" : "")
|
||||
|
|
|
@ -386,6 +386,8 @@ bool startCrashHandler(std::string appPath) {
|
|||
} else {
|
||||
qCDebug(crash_handler) << "Locating own directory by argv[0]";
|
||||
interfaceDir.setPath(QString::fromStdString(appPath));
|
||||
// argv[0] gets us the path including the binary file
|
||||
interfaceDir.cdUp();
|
||||
}
|
||||
|
||||
if (!interfaceDir.exists(CRASHPAD_HANDLER_NAME)) {
|
||||
|
|
|
@ -405,9 +405,6 @@ Menu::Menu() {
|
|||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AntiAliasing, 0, RenderScriptingInterface::getInstance()->getAntialiasingEnabled(),
|
||||
RenderScriptingInterface::getInstance(), SLOT(setAntialiasingEnabled(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, RenderScriptingInterface::getInstance()->getShadowsEnabled(),
|
||||
RenderScriptingInterface::getInstance(), SLOT(setShadowsEnabled(bool)));
|
||||
|
||||
|
|
|
@ -224,7 +224,6 @@ namespace MenuOption {
|
|||
const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar";
|
||||
const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar";
|
||||
const QString Shadows = "Shadows";
|
||||
const QString AntiAliasing = "Temporal Antialiasing (FXAA if disabled)";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
const QString NotificationSounds = "play_notification_sounds";
|
||||
const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot";
|
||||
|
|
|
@ -29,10 +29,11 @@ void PerformanceManager::setupPerformancePresetSettings(bool evaluatePlatformTie
|
|||
|
||||
// Here is the mapping between platformTier and performance profile
|
||||
const std::array<PerformanceManager::PerformancePreset, platform::Profiler::NumTiers> platformToPerformancePresetMap = { {
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
|
||||
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::MID
|
||||
PerformanceManager::PerformancePreset::HIGH // platform::Profiler::HIGH
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
|
||||
PerformanceManager::PerformancePreset::LOW_POWER, // platform::Profiler::LOW_POWER
|
||||
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::MID
|
||||
PerformanceManager::PerformancePreset::HIGH // platform::Profiler::HIGH
|
||||
} };
|
||||
|
||||
// What is our profile?
|
||||
|
@ -69,15 +70,15 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
|
|||
bool isDeferredCapable = platform::Profiler::isRenderMethodDeferredCapable();
|
||||
auto masterDisplay = platform::getDisplay(platform::getMasterDisplay());
|
||||
|
||||
// eval recommanded PPI and Scale
|
||||
float recommandedPpiScale = 1.0f;
|
||||
const float RECOMMANDED_PPI[] = { 200.0f, 120.f, 160.f, 250.f};
|
||||
// eval recommended PPI and Scale
|
||||
float recommendedPpiScale = 1.0f;
|
||||
const float RECOMMENDED_PPI[] = { 200.0f, 200.0f, 120.f, 160.f, 250.f};
|
||||
if (!masterDisplay.empty() && masterDisplay.count(platform::keys::display::ppi)) {
|
||||
float ppi = masterDisplay[platform::keys::display::ppi];
|
||||
// only scale if the actual ppi is higher than the recommended ppi
|
||||
if (ppi > RECOMMANDED_PPI[preset]) {
|
||||
if (ppi > RECOMMENDED_PPI[preset]) {
|
||||
// make sure the scale is no less than a quarter
|
||||
recommandedPpiScale = std::max(0.25f, RECOMMANDED_PPI[preset] / (float) ppi);
|
||||
recommendedPpiScale = std::max(0.25f, RECOMMENDED_PPI[preset] / (float) ppi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,39 +88,50 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
|
|||
RenderScriptingInterface::RenderMethod::DEFERRED :
|
||||
RenderScriptingInterface::RenderMethod::FORWARD ) );
|
||||
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale);
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale);
|
||||
|
||||
RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
|
||||
|
||||
DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_HIGH);
|
||||
|
||||
break;
|
||||
break;
|
||||
case PerformancePreset::MID:
|
||||
RenderScriptingInterface::getInstance()->setRenderMethod((isDeferredCapable ?
|
||||
RenderScriptingInterface::RenderMethod::DEFERRED :
|
||||
RenderScriptingInterface::RenderMethod::FORWARD));
|
||||
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale);
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale);
|
||||
|
||||
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
|
||||
DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_MEDIUM);
|
||||
|
||||
break;
|
||||
break;
|
||||
case PerformancePreset::LOW:
|
||||
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::FORWARD);
|
||||
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
|
||||
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale);
|
||||
|
||||
DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_LOW);
|
||||
|
||||
break;
|
||||
case PerformancePreset::LOW_POWER:
|
||||
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::FORWARD);
|
||||
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO);
|
||||
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale);
|
||||
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommendedPpiScale);
|
||||
|
||||
DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_LOW);
|
||||
|
||||
break;
|
||||
break;
|
||||
case PerformancePreset::UNKNOWN:
|
||||
// Intentionally unbroken.
|
||||
default:
|
||||
// Do nothing anymore
|
||||
// Do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ class PerformanceManager {
|
|||
public:
|
||||
enum PerformancePreset {
|
||||
UNKNOWN = 0, // Matching the platform Tier profiles enumeration for coherence
|
||||
LOW_POWER,
|
||||
LOW,
|
||||
MID,
|
||||
HIGH,
|
||||
|
|
|
@ -64,7 +64,11 @@ void OtherAvatar::removeOrb() {
|
|||
void OtherAvatar::updateOrbPosition() {
|
||||
if (!_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
EntityItemProperties properties;
|
||||
properties.setPosition(getHead()->getPosition());
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel->getHeadPosition(headPosition)) {
|
||||
headPosition = getWorldPosition();
|
||||
}
|
||||
properties.setPosition(headPosition);
|
||||
DependencyManager::get<EntityScriptingInterface>()->editEntity(_otherAvatarOrbMeshPlaceholderID, properties);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ PerformanceScriptingInterface::PerformancePreset PerformanceScriptingInterface::
|
|||
}
|
||||
|
||||
QStringList PerformanceScriptingInterface::getPerformancePresetNames() const {
|
||||
static const QStringList performancePresetNames = { "UNKNOWN", "LOW", "MID", "HIGH" };
|
||||
static const QStringList performancePresetNames = { "UNKNOWN", "LOW_POWER", "LOW", "MID", "HIGH" };
|
||||
return performancePresetNames;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
// PerformanceManager PerformancePreset tri state level enums
|
||||
enum PerformancePreset {
|
||||
UNKNOWN = PerformanceManager::PerformancePreset::UNKNOWN,
|
||||
LOW_POWER = PerformanceManager::PerformancePreset::LOW_POWER,
|
||||
LOW = PerformanceManager::PerformancePreset::LOW,
|
||||
MID = PerformanceManager::PerformancePreset::MID,
|
||||
HIGH = PerformanceManager::PerformancePreset::HIGH,
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "RenderScriptingInterface.h"
|
||||
|
||||
#include "LightingModel.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
|
||||
|
||||
RenderScriptingInterface* RenderScriptingInterface::getInstance() {
|
||||
|
@ -29,13 +28,14 @@ void RenderScriptingInterface::loadSettings() {
|
|||
_renderMethod = (_renderMethodSetting.get());
|
||||
_shadowsEnabled = (_shadowsEnabledSetting.get());
|
||||
_ambientOcclusionEnabled = (_ambientOcclusionEnabledSetting.get());
|
||||
_antialiasingEnabled = (_antialiasingEnabledSetting.get());
|
||||
//_antialiasingMode = (_antialiasingModeSetting.get());
|
||||
_antialiasingMode = static_cast<AntialiasingConfig::Mode>(_antialiasingModeSetting.get());
|
||||
_viewportResolutionScale = (_viewportResolutionScaleSetting.get());
|
||||
});
|
||||
forceRenderMethod((RenderMethod)_renderMethod);
|
||||
forceShadowsEnabled(_shadowsEnabled);
|
||||
forceAmbientOcclusionEnabled(_ambientOcclusionEnabled);
|
||||
forceAntialiasingEnabled(_antialiasingEnabled);
|
||||
forceAntialiasingMode(_antialiasingMode);
|
||||
forceViewportResolutionScale(_viewportResolutionScale);
|
||||
}
|
||||
|
||||
|
@ -121,35 +121,50 @@ void RenderScriptingInterface::forceAmbientOcclusionEnabled(bool enabled) {
|
|||
});
|
||||
}
|
||||
|
||||
bool RenderScriptingInterface::getAntialiasingEnabled() const {
|
||||
return _antialiasingEnabled;
|
||||
AntialiasingConfig::Mode RenderScriptingInterface::getAntialiasingMode() const {
|
||||
return _antialiasingMode;
|
||||
}
|
||||
|
||||
void RenderScriptingInterface::setAntialiasingEnabled(bool enabled) {
|
||||
if (_antialiasingEnabled != enabled) {
|
||||
forceAntialiasingEnabled(enabled);
|
||||
void RenderScriptingInterface::setAntialiasingMode(AntialiasingConfig::Mode mode) {
|
||||
if (_antialiasingMode != mode) {
|
||||
forceAntialiasingMode(mode);
|
||||
emit settingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderScriptingInterface::forceAntialiasingEnabled(bool enabled) {
|
||||
void RenderScriptingInterface::forceAntialiasingMode(AntialiasingConfig::Mode mode) {
|
||||
_renderSettingLock.withWriteLock([&] {
|
||||
_antialiasingEnabled = (enabled);
|
||||
_antialiasingEnabledSetting.set(enabled);
|
||||
_antialiasingMode = mode;
|
||||
|
||||
auto mainViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<JitterSample>("RenderMainView.JitterCam");
|
||||
auto mainViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<Antialiasing>("RenderMainView.Antialiasing");
|
||||
if (mainViewJitterCamConfig && mainViewAntialiasingConfig) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::AntiAliasing, enabled);
|
||||
if (enabled) {
|
||||
mainViewJitterCamConfig->play();
|
||||
mainViewAntialiasingConfig->setDebugFXAA(false);
|
||||
}
|
||||
else {
|
||||
mainViewJitterCamConfig->none();
|
||||
mainViewAntialiasingConfig->setDebugFXAA(true);
|
||||
switch (mode) {
|
||||
case AntialiasingConfig::Mode::NONE:
|
||||
mainViewJitterCamConfig->none();
|
||||
mainViewAntialiasingConfig->blend = 1;
|
||||
mainViewAntialiasingConfig->setDebugFXAA(false);
|
||||
break;
|
||||
case AntialiasingConfig::Mode::TAA:
|
||||
mainViewJitterCamConfig->play();
|
||||
mainViewAntialiasingConfig->blend = 0.25;
|
||||
mainViewAntialiasingConfig->setDebugFXAA(false);
|
||||
break;
|
||||
case AntialiasingConfig::Mode::FXAA:
|
||||
mainViewJitterCamConfig->none();
|
||||
mainViewAntialiasingConfig->blend = 0.25;
|
||||
mainViewAntialiasingConfig->setDebugFXAA(true);
|
||||
break;
|
||||
default:
|
||||
_antialiasingMode = AntialiasingConfig::Mode::NONE;
|
||||
mainViewJitterCamConfig->none();
|
||||
mainViewAntialiasingConfig->blend = 1;
|
||||
mainViewAntialiasingConfig->setDebugFXAA(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_antialiasingModeSetting.set(_antialiasingMode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "Application.h"
|
||||
|
||||
#include "RenderForward.h"
|
||||
#include "AntialiasingEffect.h"
|
||||
|
||||
|
||||
/*@jsdoc
|
||||
* The <code>Render</code> API enables you to configure the graphics engine.
|
||||
|
@ -27,7 +29,7 @@
|
|||
* @property {boolean} shadowsEnabled - <code>true</code> if shadows are enabled, <code>false</code> if they're disabled.
|
||||
* @property {boolean} ambientOcclusionEnabled - <code>true</code> if ambient occlusion is enabled, <code>false</code> if it's
|
||||
* disabled.
|
||||
* @property {boolean} antialiasingEnabled - <code>true</code> if anti-aliasing is enabled, <code>false</code> if it's disabled.
|
||||
* @property {integer} antialiasingMode - The active anti-aliasing mode.
|
||||
* @property {number} viewportResolutionScale - The view port resolution scale, <code>> 0.0</code>.
|
||||
*/
|
||||
class RenderScriptingInterface : public QObject {
|
||||
|
@ -35,7 +37,7 @@ class RenderScriptingInterface : public QObject {
|
|||
Q_PROPERTY(RenderMethod renderMethod READ getRenderMethod WRITE setRenderMethod NOTIFY settingsChanged)
|
||||
Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled NOTIFY settingsChanged)
|
||||
Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled NOTIFY settingsChanged)
|
||||
Q_PROPERTY(bool antialiasingEnabled READ getAntialiasingEnabled WRITE setAntialiasingEnabled NOTIFY settingsChanged)
|
||||
Q_PROPERTY(AntialiasingConfig::Mode antialiasingMode READ getAntialiasingMode WRITE setAntialiasingMode NOTIFY settingsChanged)
|
||||
Q_PROPERTY(float viewportResolutionScale READ getViewportResolutionScale WRITE setViewportResolutionScale NOTIFY settingsChanged)
|
||||
|
||||
public:
|
||||
|
@ -143,18 +145,18 @@ public slots:
|
|||
void setAmbientOcclusionEnabled(bool enabled);
|
||||
|
||||
/*@jsdoc
|
||||
* Gets whether or not anti-aliasing is enabled.
|
||||
* @function Render.getAntialiasingEnabled
|
||||
* @returns {boolean} <code>true</code> if anti-aliasing is enabled, <code>false</code> if it's disabled.
|
||||
* Gets the active anti-aliasing mode.
|
||||
* @function Render.getAntialiasingMode
|
||||
* @returns {integer} the active anti-aliasing mode.
|
||||
*/
|
||||
bool getAntialiasingEnabled() const;
|
||||
AntialiasingConfig::Mode getAntialiasingMode() const;
|
||||
|
||||
/*@jsdoc
|
||||
* Sets whether or not anti-aliasing is enabled.
|
||||
* @function Render.setAntialiasingEnabled
|
||||
* @param {boolean} enabled - <code>true</code> to enable anti-aliasing, <code>false</code> to disable.
|
||||
* Sets the active anti-aliasing mode.
|
||||
* @function Render.setAntialiasingMode
|
||||
* @param {integer} the active anti-aliasing mode.
|
||||
*/
|
||||
void setAntialiasingEnabled(bool enabled);
|
||||
void setAntialiasingMode(AntialiasingConfig::Mode mode);
|
||||
|
||||
/*@jsdoc
|
||||
* Gets the view port resolution scale.
|
||||
|
@ -192,21 +194,22 @@ private:
|
|||
int _renderMethod{ RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED };
|
||||
bool _shadowsEnabled{ true };
|
||||
bool _ambientOcclusionEnabled{ false };
|
||||
bool _antialiasingEnabled{ true };
|
||||
AntialiasingConfig::Mode _antialiasingMode{ AntialiasingConfig::Mode::TAA };
|
||||
float _viewportResolutionScale{ 1.0f };
|
||||
|
||||
// Actual settings saved on disk
|
||||
Setting::Handle<int> _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED };
|
||||
Setting::Handle<bool> _shadowsEnabledSetting { "shadowsEnabled", true };
|
||||
Setting::Handle<bool> _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false };
|
||||
Setting::Handle<bool> _antialiasingEnabledSetting { "antialiasingEnabled", true };
|
||||
//Setting::Handle<AntialiasingConfig::Mode> _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::TAA };
|
||||
Setting::Handle<int> _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::TAA };
|
||||
Setting::Handle<float> _viewportResolutionScaleSetting { "viewportResolutionScale", 1.0f };
|
||||
|
||||
// Force assign both setting AND runtime value to the parameter value
|
||||
void forceRenderMethod(RenderMethod renderMethod);
|
||||
void forceShadowsEnabled(bool enabled);
|
||||
void forceAmbientOcclusionEnabled(bool enabled);
|
||||
void forceAntialiasingEnabled(bool enabled);
|
||||
void forceAntialiasingMode(AntialiasingConfig::Mode mode);
|
||||
void forceViewportResolutionScale(float scale);
|
||||
|
||||
static std::once_flag registry_flag;
|
||||
|
|
|
@ -551,7 +551,7 @@ void setupPreferences() {
|
|||
auto getter = [nodeListWeak] {
|
||||
auto nodeList = nodeListWeak.lock();
|
||||
if (nodeList) {
|
||||
return static_cast<int>(nodeList->getSocketLocalPort());
|
||||
return static_cast<int>(nodeList->getSocketLocalPort(SocketType::UDP));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ void setupPreferences() {
|
|||
auto setter = [nodeListWeak](int preset) {
|
||||
auto nodeList = nodeListWeak.lock();
|
||||
if (nodeList) {
|
||||
nodeList->setSocketLocalPort(static_cast<quint16>(preset));
|
||||
nodeList->setSocketLocalPort(SocketType::UDP, static_cast<quint16>(preset));
|
||||
}
|
||||
};
|
||||
auto preference = new IntSpinnerPreference(NETWORKING, "Listening Port", getter, setter);
|
||||
|
|
|
@ -69,7 +69,7 @@ if (WIN32)
|
|||
set(OPENSSL_ROOT_DIR ${SSL_DIR})
|
||||
message("SSL dir is ${SSL_DIR}")
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(OpenSSL 1.1.0 REQUIRED)
|
||||
|
||||
message("-- Found OpenSSL Libs ${OPENSSL_LIBRARIES}")
|
||||
|
||||
|
@ -105,7 +105,7 @@ endif()
|
|||
|
||||
if (APPLE)
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(OpenSSL 1.1.0 REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Qt5 COMPONENTS Core Gui Qml Quick QuickControls2 Network REQUIRED)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 1/22/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -363,7 +364,7 @@ AudioClient::AudioClient() {
|
|||
|
||||
configureReverb();
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
configureWebrtc();
|
||||
#endif
|
||||
|
||||
|
@ -941,7 +942,7 @@ void AudioClient::Gate::flush() {
|
|||
|
||||
|
||||
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
setMuted(true);
|
||||
|
||||
// have the audio scripting interface emit a signal to say we were muted by the mixer
|
||||
|
@ -988,7 +989,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
|
||||
_selectedCodecName = selectedCodecName;
|
||||
|
||||
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
|
||||
qCDebug(audioclient) << "Selected codec:" << _selectedCodecName << "; Is stereo input:" << _isStereoInput;
|
||||
|
||||
// release any old codec encoder/decoder first...
|
||||
if (_codec && _encoder) {
|
||||
|
@ -1004,7 +1005,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
_codec = plugin;
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
|
||||
qCDebug(audioclient) << "Selected codec plugin:" << _codec.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1142,7 +1143,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
|
||||
static void deinterleaveToFloat(const int16_t* src, float* const* dst, int numFrames, int numChannels) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
@ -1175,7 +1176,9 @@ void AudioClient::configureWebrtc() {
|
|||
config.high_pass_filter.enabled = false;
|
||||
config.echo_canceller.enabled = true;
|
||||
config.echo_canceller.mobile_mode = false;
|
||||
#if defined(WEBRTC_LEGACY)
|
||||
config.echo_canceller.use_legacy_aec = false;
|
||||
#endif
|
||||
config.noise_suppression.enabled = false;
|
||||
config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kModerate;
|
||||
config.voice_detection.enabled = false;
|
||||
|
@ -1261,12 +1264,12 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
|
|||
}
|
||||
}
|
||||
|
||||
#endif // WEBRTC_ENABLED
|
||||
#endif // WEBRTC_AUDIO
|
||||
|
||||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||
if ((_isMuted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1354,7 +1357,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
|
||||
bool audioGateOpen = false;
|
||||
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
|
||||
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
||||
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
|
@ -1375,7 +1378,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
}
|
||||
|
||||
// loudness after mute/gate
|
||||
_lastInputLoudness = (_muted || !audioGateOpen) ? 0.0f : _lastRawInputLoudness;
|
||||
_lastInputLoudness = (_isMuted || !audioGateOpen) ? 0.0f : _lastRawInputLoudness;
|
||||
|
||||
// detect gate opening and closing
|
||||
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
|
||||
|
@ -1462,7 +1465,7 @@ void AudioClient::handleMicAudioInput() {
|
|||
}
|
||||
isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
if (_isAECEnabled) {
|
||||
processWebrtcNearEnd(inputAudioSamples.get(), inputSamplesRequired / _inputFormat.channelCount(),
|
||||
_inputFormat.channelCount(), _inputFormat.sampleRate());
|
||||
|
@ -1479,7 +1482,7 @@ void AudioClient::handleMicAudioInput() {
|
|||
|
||||
emit inputLoudnessChanged(_lastSmoothedRawInputLoudness, isClipping);
|
||||
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
possibleResampling(_inputToNetworkResampler,
|
||||
inputAudioSamples.get(), networkAudioSamples,
|
||||
inputSamplesRequired, numNetworkSamples,
|
||||
|
@ -1745,10 +1748,10 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
}
|
||||
|
||||
void AudioClient::setMuted(bool muted, bool emitSignal) {
|
||||
if (_muted != muted) {
|
||||
_muted = muted;
|
||||
if (_isMuted != muted) {
|
||||
_isMuted = muted;
|
||||
if (emitSignal) {
|
||||
emit muteToggled(_muted);
|
||||
emit muteToggled(_isMuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1893,7 +1896,6 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice
|
|||
|
||||
if (_dummyAudioInput) {
|
||||
_dummyAudioInput->stop();
|
||||
|
||||
_dummyAudioInput->deleteLater();
|
||||
_dummyAudioInput = NULL;
|
||||
}
|
||||
|
@ -2420,7 +2422,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
// limit the audio
|
||||
_audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
if (_audio->_isAECEnabled) {
|
||||
_audio->processWebrtcFarEnd(scratchBuffer, framesPopped, OUTPUT_CHANNEL_COUNT, _audio->_outputFormat.sampleRate());
|
||||
}
|
||||
|
@ -2505,4 +2507,4 @@ void AudioClient::setInputVolume(float volume, bool emitSignal) {
|
|||
emit inputVolumeChanged(_audioInput->volume());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,12 @@
|
|||
#include "AudioFileWav.h"
|
||||
#include "HifiAudioDeviceInfo.h"
|
||||
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
# define WEBRTC_APM_DEBUG_DUMP 0
|
||||
# include <modules/audio_processing/include/audio_processing.h>
|
||||
# include "modules/audio_processing/audio_processing_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4273 )
|
||||
|
@ -212,7 +218,7 @@ public slots:
|
|||
void audioMixerKilled();
|
||||
|
||||
void setMuted(bool muted, bool emitSignal = true);
|
||||
bool isMuted() { return _muted; }
|
||||
bool isMuted() { return _isMuted; }
|
||||
|
||||
virtual bool setIsStereoInput(bool stereo) override;
|
||||
virtual bool isStereoInput() override { return _isStereoInput; }
|
||||
|
@ -405,7 +411,7 @@ private:
|
|||
float _timeSinceLastClip{ -1.0f };
|
||||
int _totalInputAudioSamples;
|
||||
|
||||
bool _muted{ false };
|
||||
bool _isMuted{ false };
|
||||
bool _shouldEchoLocally{ false };
|
||||
bool _shouldEchoToServer{ false };
|
||||
bool _isNoiseGateEnabled{ true };
|
||||
|
@ -452,7 +458,7 @@ private:
|
|||
void updateReverbOptions();
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
static const int WEBRTC_SAMPLE_RATE_MAX = 96000;
|
||||
static const int WEBRTC_CHANNELS_MAX = 2;
|
||||
static const int WEBRTC_FRAMES_MAX = webrtc::AudioProcessing::kChunkSizeMs * WEBRTC_SAMPLE_RATE_MAX / 1000;
|
||||
|
|
|
@ -167,7 +167,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
bool packetPCM = codecInPacket == "pcm" || codecInPacket == "";
|
||||
if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) {
|
||||
auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead());
|
||||
parseAudioData(message.getType(), afterProperties);
|
||||
parseAudioData(afterProperties);
|
||||
_mismatchedAudioCodecCount = 0;
|
||||
|
||||
} else {
|
||||
|
@ -267,7 +267,7 @@ int InboundAudioStream::lostAudioData(int numPackets) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
int InboundAudioStream::parseAudioData(const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
|
|
|
@ -132,7 +132,7 @@ protected:
|
|||
|
||||
/// parses the audio data in the network packet.
|
||||
/// default implementation assumes packet contains raw audio samples after stream properties
|
||||
virtual int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties);
|
||||
virtual int parseAudioData(const QByteArray& packetAfterStreamProperties);
|
||||
|
||||
/// produces audio data for lost network packets.
|
||||
virtual int lostAudioData(int numPackets);
|
||||
|
|
|
@ -61,7 +61,7 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
int MixedProcessedAudioStream::parseAudioData(const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
protected:
|
||||
int writeDroppableSilentFrames(int silentFrames) override;
|
||||
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override;
|
||||
int parseAudioData(const QByteArray& packetAfterStreamProperties) override;
|
||||
int lostAudioData(int numPackets) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -338,6 +338,7 @@ void HTTPConnection::readHeaders() {
|
|||
|
||||
QByteArray clength = requestHeader("Content-Length");
|
||||
if (clength.isEmpty()) {
|
||||
_requestContent = MemoryStorage::make(0);
|
||||
_parentManager->handleHTTPRequest(this, _requestUrl);
|
||||
|
||||
} else {
|
||||
|
|
|
@ -31,7 +31,7 @@ HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const
|
|||
_port(port)
|
||||
{
|
||||
bindSocket();
|
||||
|
||||
|
||||
_isListeningTimer = new QTimer(this);
|
||||
connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening);
|
||||
_isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS);
|
||||
|
@ -39,7 +39,7 @@ HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const
|
|||
|
||||
void HTTPManager::incomingConnection(qintptr socketDescriptor) {
|
||||
QTcpSocket* socket = new QTcpSocket(this);
|
||||
|
||||
|
||||
if (socket->setSocketDescriptor(socketDescriptor)) {
|
||||
new HTTPConnection(socket, this);
|
||||
} else {
|
||||
|
@ -60,7 +60,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
// so we don't need to attempt to do so in the document root
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!_documentRoot.isEmpty()) {
|
||||
// check to see if there is a file to serve from the document root for this path
|
||||
QString subPath = url.path();
|
||||
|
@ -88,22 +88,22 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
// this could be a directory with a trailing slash
|
||||
// send a redirect to the path with a slash so we can
|
||||
QString redirectLocation = '/' + subPath + '/';
|
||||
|
||||
|
||||
if (!url.query().isEmpty()) {
|
||||
redirectLocation += "?" + url.query();
|
||||
}
|
||||
|
||||
|
||||
QHash<QByteArray, QByteArray> redirectHeader;
|
||||
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
|
||||
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode302, "", HTTPConnection::DefaultContentType, redirectHeader);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// if the last thing is a trailing slash then we want to look for index file
|
||||
if (subPath.endsWith('/') || subPath.size() == 0) {
|
||||
QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml";
|
||||
|
||||
|
||||
foreach (const QString& possibleIndexFilename, possibleIndexFiles) {
|
||||
if (QFileInfo(absoluteFilePath + possibleIndexFilename).exists()) {
|
||||
filePath = absoluteFilePath + possibleIndexFilename;
|
||||
|
@ -111,64 +111,65 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!filePath.isEmpty()) {
|
||||
// file exists, serve it
|
||||
static QMimeDatabase mimeDatabase;
|
||||
|
||||
|
||||
auto localFile = std::unique_ptr<QFile>(new QFile(filePath));
|
||||
localFile->open(QIODevice::ReadOnly);
|
||||
QByteArray localFileData;
|
||||
|
||||
|
||||
QFileInfo localFileInfo(filePath);
|
||||
|
||||
|
||||
if (localFileInfo.completeSuffix() == "shtml") {
|
||||
localFileData = localFile->readAll();
|
||||
// this is a file that may have some SSI statements
|
||||
// the only thing we support is the include directive, but check the contents for that
|
||||
|
||||
|
||||
// setup our static QRegExp that will catch <!--#include virtual ... --> and <!--#include file .. --> directives
|
||||
const QString includeRegExpString = "<!--\\s*#include\\s+(virtual|file)\\s?=\\s?\"(\\S+)\"\\s*-->";
|
||||
QRegExp includeRegExp(includeRegExpString);
|
||||
|
||||
|
||||
int matchPosition = 0;
|
||||
|
||||
|
||||
QString localFileString(localFileData);
|
||||
|
||||
|
||||
while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) {
|
||||
// check if this is a file or vitual include
|
||||
bool isFileInclude = includeRegExp.cap(1) == "file";
|
||||
|
||||
|
||||
// setup the correct file path for the included file
|
||||
QString includeFilePath = isFileInclude
|
||||
? localFileInfo.canonicalPath() + "/" + includeRegExp.cap(2)
|
||||
: _documentRoot + includeRegExp.cap(2);
|
||||
|
||||
|
||||
QString replacementString;
|
||||
|
||||
|
||||
if (QFileInfo(includeFilePath).isFile()) {
|
||||
|
||||
|
||||
QFile includedFile(includeFilePath);
|
||||
includedFile.open(QIODevice::ReadOnly);
|
||||
|
||||
|
||||
replacementString = QString(includedFile.readAll());
|
||||
} else {
|
||||
qCDebug(embeddedwebserver) << "SSI include directive referenced a missing file:" << includeFilePath;
|
||||
}
|
||||
|
||||
|
||||
// replace the match with the contents of the file, or an empty string if the file was not found
|
||||
localFileString.replace(matchPosition, includeRegExp.matchedLength(), replacementString);
|
||||
|
||||
|
||||
// push the match position forward so we can check the next match
|
||||
matchPosition += includeRegExp.matchedLength();
|
||||
}
|
||||
|
||||
|
||||
localFileData = localFileString.toLocal8Bit();
|
||||
}
|
||||
|
||||
// if this is an shtml file just make the MIME type match HTML so browsers aren't confused
|
||||
// if this is an shtml, html or htm file just make the MIME type match HTML so browsers aren't confused
|
||||
// otherwise use the mimeDatabase to look it up
|
||||
auto mimeType = localFileInfo.suffix() == "shtml"
|
||||
auto suffix = localFileInfo.suffix();
|
||||
auto mimeType = (suffix == "shtml" || suffix == "html" || suffix == "htm")
|
||||
? QString { "text/html" }
|
||||
: mimeDatabase.mimeTypeForFile(filePath).name();
|
||||
|
||||
|
@ -181,10 +182,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url,
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// respond with a 404
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -201,10 +202,10 @@ void HTTPManager::isTcpServerListening() {
|
|||
|
||||
bool HTTPManager::bindSocket() {
|
||||
qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port);
|
||||
|
||||
|
||||
if (listen(_listenAddress, _port)) {
|
||||
qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort();
|
||||
|
||||
|
||||
return true;
|
||||
} else {
|
||||
QString errorMessage = "Failed to open HTTP server socket: " + errorString() + ", can't continue";
|
||||
|
|
|
@ -151,45 +151,54 @@ void GLBackend::init() {
|
|||
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
|
||||
unsigned int mem;
|
||||
|
||||
// Do not try to get texture memory information on unsupported systems.
|
||||
#if defined(Q_OS_ANDROID) || defined(USE_GLES) || defined(Q_OS_DARWIN)
|
||||
qCDebug(gpugllogging) << "Automatic texture memory not supported in this configuration";
|
||||
_videoCard = Unknown;
|
||||
_dedicatedMemory = (size_t)(gpu->getMemory()) * BYTES_PER_MIB;
|
||||
_totalMemory = _dedicatedMemory;
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES) && !defined(Q_OS_DARWIN)
|
||||
if (vendor.contains("NVIDIA") ) {
|
||||
qCDebug(gpugllogging) << "NVIDIA card detected";
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX);
|
||||
GL_GET_INTEGER(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX);
|
||||
#endif
|
||||
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: " << GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: " << GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX;
|
||||
qCDebug(gpugllogging) << "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: " << GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
|
||||
|
||||
_totalMemory = GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX * BYTES_PER_KIB;
|
||||
_dedicatedMemory = GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX * BYTES_PER_KIB;
|
||||
_totalMemory = (size_t)(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX) * BYTES_PER_KIB;
|
||||
_dedicatedMemory = (size_t)(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX) * BYTES_PER_KIB;
|
||||
_videoCard = NVIDIA;
|
||||
|
||||
|
||||
} else if (vendor.contains("ATI")) {
|
||||
qCDebug(gpugllogging) << "ATI card detected";
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
GL_GET_INTEGER(TEXTURE_FREE_MEMORY_ATI);
|
||||
#endif
|
||||
|
||||
_totalMemory = TEXTURE_FREE_MEMORY_ATI * BYTES_PER_KIB;
|
||||
GL_GET_INTEGER(TEXTURE_FREE_MEMORY_ATI);
|
||||
|
||||
// We are actually getting free memory instead of total memory
|
||||
_totalMemory = (size_t)(TEXTURE_FREE_MEMORY_ATI) * BYTES_PER_KIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = ATI;
|
||||
} else if ( ::gl::queryCurrentRendererIntegerMESA(GLX_RENDERER_VIDEO_MEMORY_MESA, &mem) ) {
|
||||
// This works only on Linux. queryCurrentRendererIntegerMESA will return false if the
|
||||
// function is not supported because we're not on Linux, or for any other reason.
|
||||
qCDebug(gpugllogging) << "MESA card detected";
|
||||
_totalMemory = mem * BYTES_PER_MIB;
|
||||
_totalMemory = (size_t)(mem) * BYTES_PER_MIB;
|
||||
_dedicatedMemory = _totalMemory;
|
||||
_videoCard = MESA;
|
||||
} else {
|
||||
qCCritical(gpugllogging) << "Don't know how to get memory for OpenGL vendor " << vendor << "; renderer " << renderer << ", trying fallback";
|
||||
_videoCard = Unknown;
|
||||
_dedicatedMemory = gpu->getMemory();
|
||||
_dedicatedMemory = (size_t)(gpu->getMemory()) * BYTES_PER_MIB;
|
||||
_totalMemory = _dedicatedMemory;
|
||||
}
|
||||
#endif
|
||||
|
||||
qCDebug(gpugllogging) << "dedicated: " << _dedicatedMemory;
|
||||
qCDebug(gpugllogging) << "total: " << _totalMemory;
|
||||
|
@ -228,12 +237,12 @@ size_t GLBackend::getAvailableMemory() {
|
|||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &mem[0]);
|
||||
#endif
|
||||
return mem[0] * BYTES_PER_KIB;
|
||||
return (size_t)(mem[0]) * BYTES_PER_KIB;
|
||||
case ATI:
|
||||
#if !defined(Q_OS_ANDROID) && !defined(USE_GLES)
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &mem[0]);
|
||||
#endif
|
||||
return mem[0] * BYTES_PER_KIB;
|
||||
return (size_t)(mem[0]) * BYTES_PER_KIB;
|
||||
case MESA:
|
||||
return 0; // Don't know the current value
|
||||
case Unknown:
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
set(TARGET_NAME networking)
|
||||
setup_hifi_library(Network)
|
||||
setup_hifi_library(Network WebSockets)
|
||||
link_hifi_libraries(shared platform)
|
||||
|
||||
target_openssl()
|
||||
target_tbb()
|
||||
|
||||
if (WIN32 OR (UNIX AND NOT APPLE))
|
||||
target_webrtc()
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# we need ws2_32.lib on windows, but it's static so we don't bubble it up
|
||||
target_link_libraries(${TARGET_NAME} ws2_32.lib)
|
||||
# Libraries needed for WebRTC: security.lib winmm.lib
|
||||
target_link_libraries(${TARGET_NAME} ws2_32.lib security.lib winmm.lib)
|
||||
elseif(APPLE)
|
||||
# IOKit is needed for getting machine fingerprint
|
||||
find_library(FRAMEWORK_IOKIT IOKit)
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
// libraries/networking/src
|
||||
//
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
#include "BaseAssetScriptingInterface.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMimeDatabase>
|
||||
#include <QThread>
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
DomainHandler::DomainHandler(QObject* parent) :
|
||||
QObject(parent),
|
||||
_sockAddr(SockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_sockAddr(SockAddr(SocketType::UDP, QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_icePeer(this),
|
||||
_settingsTimer(this),
|
||||
_apiRefreshTimer(this)
|
||||
|
@ -282,7 +282,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
|||
|
||||
SockAddr* replaceableSockAddr = &_iceServerSockAddr;
|
||||
replaceableSockAddr->~SockAddr();
|
||||
replaceableSockAddr = new (replaceableSockAddr) SockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
||||
replaceableSockAddr = new (replaceableSockAddr) SockAddr(SocketType::UDP, iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
||||
_iceServerSockAddr.setObjectName("IceServer");
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -367,7 +367,7 @@ void DomainHandler::setIsConnected(bool isConnected) {
|
|||
emit connectedToDomain(_domainURL);
|
||||
|
||||
// FIXME: Reinstate the requestDomainSettings() call here in version 2021.2.0 instead of having it in
|
||||
// NodeList::processDomainServerList().
|
||||
// NodeList::processDomainList().
|
||||
/*
|
||||
if (_domainURL.scheme() == URL_SCHEME_VIRCADIA && !_domainURL.host().isEmpty()) {
|
||||
// we've connected to new domain - time to ask it for global settings
|
||||
|
|
|
@ -41,7 +41,15 @@ const unsigned short DEFAULT_DOMAIN_SERVER_PORT =
|
|||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("HIFI_DOMAIN_SERVER_PORT")
|
||||
.toUShort()
|
||||
: 40102;
|
||||
: 40102; // UDP
|
||||
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_WS_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
.contains("VIRCADIA_DOMAIN_SERVER_WS_PORT")
|
||||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("VIRCADIA_DOMAIN_SERVER_WS_PORT")
|
||||
.toUShort()
|
||||
: 40102; // TCP
|
||||
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
|
@ -235,7 +243,7 @@ public:
|
|||
};
|
||||
|
||||
public slots:
|
||||
void setURLAndID(QUrl domainURL, QUuid id);
|
||||
void setURLAndID(QUrl domainURL, QUuid domainID);
|
||||
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
||||
|
||||
void processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList);
|
||||
|
@ -245,7 +253,7 @@ public slots:
|
|||
void processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
// sets domain handler in error state.
|
||||
void setRedirectErrorState(QUrl errorUrl, QString reasonMessage = "", int reason = -1, const QString& extraInfo = "");
|
||||
void setRedirectErrorState(QUrl errorUrl, QString reasonMessage = "", int reasonCode = -1, const QString& extraInfo = "");
|
||||
|
||||
bool isInErrorState() { return _isInErrorState; }
|
||||
|
||||
|
@ -271,7 +279,7 @@ signals:
|
|||
void settingsReceived(const QJsonObject& domainSettingsObject);
|
||||
void settingsReceiveFail();
|
||||
|
||||
void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo);
|
||||
void domainConnectionRefused(QString reasonMessage, int reasonCode, const QString& extraInfo);
|
||||
void redirectToErrorDomainURL(QUrl errorDomainURL);
|
||||
void redirectErrorStateChanged(bool isInErrorState);
|
||||
|
||||
|
|
|
@ -51,17 +51,17 @@ using namespace std::chrono_literals;
|
|||
static const std::chrono::milliseconds CONNECTION_RATE_INTERVAL_MS = 1s;
|
||||
|
||||
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
||||
_nodeSocket(this),
|
||||
_nodeSocket(this, true),
|
||||
_packetReceiver(new PacketReceiver(this))
|
||||
{
|
||||
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
||||
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
|
||||
quint16 assignedPort = _nodeSocket.localPort();
|
||||
_nodeSocket.bind(SocketType::UDP, QHostAddress::AnyIPv4, port);
|
||||
quint16 assignedPort = _nodeSocket.localPort(SocketType::UDP);
|
||||
if (socketListenPort != INVALID_PORT && socketListenPort != 0 && socketListenPort != assignedPort) {
|
||||
qCCritical(networking) << "PAGE: NodeList is unable to assign requested port of" << socketListenPort;
|
||||
qCCritical(networking) << "PAGE: NodeList is unable to assign requested UDP port of" << socketListenPort;
|
||||
}
|
||||
qCDebug(networking) << "NodeList socket is listening on" << assignedPort;
|
||||
qCDebug(networking) << "NodeList UDP socket is listening on" << assignedPort;
|
||||
|
||||
if (dtlsListenPort != INVALID_PORT) {
|
||||
// only create the DTLS socket during constructor if a custom port is passed
|
||||
|
@ -74,6 +74,8 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
|||
qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
|
||||
}
|
||||
|
||||
_nodeSocket.bind(SocketType::WebRTC, QHostAddress::AnyIPv4);
|
||||
|
||||
// check for local socket updates every so often
|
||||
const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;
|
||||
QTimer* localSocketUpdate = new QTimer(this);
|
||||
|
@ -205,15 +207,20 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
|
|||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
|
||||
void LimitedNodeList::setSocketLocalPort(SocketType socketType, quint16 socketLocalPort) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setSocketLocalPort", Qt::QueuedConnection,
|
||||
Q_ARG(quint16, socketLocalPort));
|
||||
return;
|
||||
}
|
||||
if (_nodeSocket.localPort() != socketLocalPort) {
|
||||
_nodeSocket.rebind(socketLocalPort);
|
||||
LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
|
||||
if (_nodeSocket.localPort(socketType) != socketLocalPort) {
|
||||
_nodeSocket.rebind(socketType, socketLocalPort);
|
||||
if (socketType == SocketType::UDP) {
|
||||
LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
|
||||
} else {
|
||||
// WEBRTC TODO: Add WebRTC equivalent?
|
||||
qCWarning(networking_webrtc) << "LIMITED_NODELIST_LOCAL_PORT not set for WebRTC socket";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +241,12 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
|||
return *_dtlsSocket;
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const WebRTCSocket* LimitedNodeList::getWebRTCSocket() {
|
||||
return _nodeSocket.getWebRTCSocket();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool LimitedNodeList::isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode) {
|
||||
// We track bandwidth when doing packet verification to avoid needing to do a node lookup
|
||||
// later when we already do it in packetSourceAndHashMatchAndTrackBandwidth. A node lookup
|
||||
|
@ -492,8 +505,8 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
|
|||
}
|
||||
return bytesSent;
|
||||
} else {
|
||||
qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode
|
||||
<< " - not sending.";
|
||||
qCDebug(networking) << "LimitedNodeList::sendUnreliableUnorderedPacketList called without active socket for node"
|
||||
<< destinationNode << " - not sending.";
|
||||
return ERROR_SENDING_PACKET_BYTES;
|
||||
}
|
||||
}
|
||||
|
@ -1106,7 +1119,7 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
|
|||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
_publicSockAddr = SockAddr(newPublicAddress, newPublicPort);
|
||||
_publicSockAddr = SockAddr(SocketType::UDP, newPublicAddress, newPublicPort);
|
||||
|
||||
if (!_hasCompletedInitialSTUN) {
|
||||
// if we're here we have definitely completed our initial STUN sequence
|
||||
|
@ -1187,7 +1200,7 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) {
|
|||
qCDebug(networking) << "LimitedNodeList public socket will be set with local port and null QHostAddress.";
|
||||
|
||||
// reset the public address and port to a null address
|
||||
_publicSockAddr = SockAddr(QHostAddress(), _nodeSocket.localPort());
|
||||
_publicSockAddr = SockAddr(SocketType::UDP, QHostAddress(), _nodeSocket.localPort(SocketType::UDP));
|
||||
|
||||
// we have changed the publicSockAddr, so emit our signal
|
||||
emit publicSockAddrChanged(_publicSockAddr);
|
||||
|
@ -1214,7 +1227,7 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) {
|
|||
void LimitedNodeList::updateLocalSocket() {
|
||||
// when update is called, if the local socket is empty then start with the guessed local socket
|
||||
if (_localSockAddr.isNull()) {
|
||||
setLocalSocket(SockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() });
|
||||
setLocalSocket(SockAddr { SocketType::UDP, getGuessedLocalAddress(), _nodeSocket.localPort(SocketType::UDP) });
|
||||
}
|
||||
|
||||
// attempt to use Google's DNS to confirm that local IP
|
||||
|
@ -1237,7 +1250,7 @@ void LimitedNodeList::connectedForLocalSocketTest() {
|
|||
auto localHostAddress = localIPTestSocket->localAddress();
|
||||
|
||||
if (localHostAddress.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
setLocalSocket(SockAddr { localHostAddress, _nodeSocket.localPort() });
|
||||
setLocalSocket(SockAddr { SocketType::UDP, localHostAddress, _nodeSocket.localPort(SocketType::UDP) });
|
||||
_hasTCPCheckedLocalSocket = true;
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1266,7 @@ void LimitedNodeList::errorTestingLocalSocket() {
|
|||
// error connecting to the test socket - if we've never set our local socket using this test socket
|
||||
// then use our possibly updated guessed local address as fallback
|
||||
if (!_hasTCPCheckedLocalSocket) {
|
||||
setLocalSocket(SockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() });
|
||||
setLocalSocket(SockAddr { SocketType::UDP, getGuessedLocalAddress(), _nodeSocket.localPort(SocketType::UDP) });
|
||||
qCCritical(networking) << "PAGE: Can't connect to Google DNS service via TCP, falling back to guessed local address"
|
||||
<< getLocalSockAddr();
|
||||
}
|
||||
|
@ -1273,8 +1286,8 @@ void LimitedNodeList::setLocalSocket(const SockAddr& sockAddr) {
|
|||
_localSockAddr = sockAddr;
|
||||
if (_hasTCPCheckedLocalSocket) { // Force a port change for NAT:
|
||||
reset("local socket change");
|
||||
_nodeSocket.rebind(0);
|
||||
_localSockAddr.setPort(_nodeSocket.localPort());
|
||||
_nodeSocket.rebind(SocketType::UDP, 0);
|
||||
_localSockAddr.setPort(_nodeSocket.localPort(SocketType::UDP));
|
||||
qCInfo(networking) << "Local port changed to" << _localSockAddr.getPort();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "DomainHandler.h"
|
||||
#include "NetworkingConstants.h"
|
||||
#include "Node.h"
|
||||
#include "NLPacket.h"
|
||||
|
@ -122,7 +121,7 @@ public:
|
|||
QUuid getSessionUUID() const;
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
Node::LocalID getSessionLocalID() const;
|
||||
void setSessionLocalID(Node::LocalID localID);
|
||||
void setSessionLocalID(Node::LocalID sessionLocalID);
|
||||
|
||||
void setPermissions(const NodePermissions& newPermissions);
|
||||
bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); }
|
||||
|
@ -136,10 +135,14 @@ public:
|
|||
bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); }
|
||||
bool getThisNodeCanRezAvatarEntities() const { return _permissions.can(NodePermissions::Permission::canRezAvatarEntities); }
|
||||
|
||||
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
||||
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
|
||||
quint16 getSocketLocalPort(SocketType socketType) const { return _nodeSocket.localPort(socketType); }
|
||||
Q_INVOKABLE void setSocketLocalPort(SocketType socketType, quint16 socketLocalPort);
|
||||
|
||||
QUdpSocket& getDTLSSocket();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const WebRTCSocket* getWebRTCSocket();
|
||||
#endif
|
||||
|
||||
|
||||
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
|
||||
|
||||
|
@ -420,7 +423,6 @@ protected:
|
|||
|
||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode,
|
||||
const SockAddr& overridenSockAddr);
|
||||
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
void setLocalSocket(const SockAddr& sockAddr);
|
||||
|
||||
|
@ -447,7 +449,7 @@ protected:
|
|||
QUdpSocket* _dtlsSocket { nullptr };
|
||||
SockAddr _localSockAddr;
|
||||
SockAddr _publicSockAddr;
|
||||
SockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
|
||||
SockAddr _stunSockAddr { SocketType::UDP, STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
|
||||
bool _hasTCPCheckedLocalSocket { false };
|
||||
bool _useAuthentication { true };
|
||||
|
||||
|
@ -487,6 +489,8 @@ private slots:
|
|||
void addSTUNHandlerToUnfiltered(); // called once STUN socket known
|
||||
|
||||
private:
|
||||
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
|
||||
|
||||
mutable QReadWriteLock _sessionUUIDLock;
|
||||
QUuid _sessionUUID;
|
||||
using LocalIDMapping = tbb::concurrent_unordered_map<Node::LocalID, SharedNodePointer>;
|
||||
|
|
|
@ -35,7 +35,6 @@ private:
|
|||
|
||||
virtual std::unique_ptr<udt::Packet> createPacket() override;
|
||||
|
||||
|
||||
PacketVersion _packetVersion;
|
||||
NLPacket::LocalID _sourceID;
|
||||
};
|
||||
|
|
|
@ -16,3 +16,4 @@ Q_LOGGING_CATEGORY(networking_ice, "hifi.networking.ice")
|
|||
Q_LOGGING_CATEGORY(resourceLog, "hifi.networking.resource")
|
||||
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")
|
||||
Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client")
|
||||
Q_LOGGING_CATEGORY(networking_webrtc, "hifi.networking.webrtc")
|
||||
|
|
|
@ -19,5 +19,6 @@ Q_DECLARE_LOGGING_CATEGORY(networking)
|
|||
Q_DECLARE_LOGGING_CATEGORY(networking_ice)
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking_webrtc)
|
||||
|
||||
#endif // hifi_NetworkLogging_h
|
||||
|
|
|
@ -117,7 +117,7 @@ void NetworkPeer::setActiveSocket(SockAddr* discoveredSocket) {
|
|||
// we have an active socket, stop our ping timer
|
||||
stopPingTimer();
|
||||
|
||||
// we're now considered connected to this peer - reset the number of connection attemps
|
||||
// we're now considered connected to this peer - reset the number of connection attempts
|
||||
resetConnectionAttempts();
|
||||
|
||||
if (_activeSocket) {
|
||||
|
|
|
@ -46,6 +46,22 @@ static const QHash<NodeType_t, QString> TYPE_NAME_HASH {
|
|||
{ NodeType::Unassigned, "Unassigned" }
|
||||
};
|
||||
|
||||
static const QHash<NodeType_t, QString> TYPE_CHAR_HASH {
|
||||
{ NodeType::DomainServer, "D" },
|
||||
{ NodeType::EntityServer, "o" },
|
||||
{ NodeType::Agent, "I" },
|
||||
{ NodeType::AudioMixer, "M" },
|
||||
{ NodeType::AvatarMixer, "W" },
|
||||
{ NodeType::AssetServer, "A" },
|
||||
{ NodeType::MessagesMixer, "m" },
|
||||
{ NodeType::EntityScriptServer, "S" },
|
||||
{ NodeType::UpstreamAudioMixer, "B" },
|
||||
{ NodeType::UpstreamAvatarMixer, "C" },
|
||||
{ NodeType::DownstreamAudioMixer, "a" },
|
||||
{ NodeType::DownstreamAvatarMixer, "w" },
|
||||
{ NodeType::Unassigned, QChar(1) }
|
||||
};
|
||||
|
||||
const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
||||
const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType);
|
||||
return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||
|
@ -85,6 +101,9 @@ NodeType_t NodeType::fromString(QString type) {
|
|||
return TYPE_NAME_HASH.key(type, NodeType::Unassigned);
|
||||
}
|
||||
|
||||
NodeType_t NodeType::fromChar(QChar type) {
|
||||
return TYPE_CHAR_HASH.key(type, NodeType::Unassigned);
|
||||
}
|
||||
|
||||
Node::Node(const QUuid& uuid, NodeType_t type, const SockAddr& publicSocket,
|
||||
const SockAddr& localSocket, QObject* parent) :
|
||||
|
@ -177,7 +196,9 @@ bool Node::isIgnoringNodeWithID(const QUuid& nodeID) const {
|
|||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||
out << node._type;
|
||||
out << node._uuid;
|
||||
out << node._publicSocket.getType();
|
||||
out << node._publicSocket;
|
||||
out << node._localSocket.getType();
|
||||
out << node._localSocket;
|
||||
out << node._permissions;
|
||||
out << node._isReplicated;
|
||||
|
@ -186,10 +207,15 @@ QDataStream& operator<<(QDataStream& out, const Node& node) {
|
|||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& in, Node& node) {
|
||||
SocketType publicSocketType, localSocketType;
|
||||
in >> node._type;
|
||||
in >> node._uuid;
|
||||
in >> publicSocketType;
|
||||
in >> node._publicSocket;
|
||||
node._publicSocket.setType(publicSocketType);
|
||||
in >> localSocketType;
|
||||
in >> node._localSocket;
|
||||
node._localSocket.setType(localSocketType);
|
||||
in >> node._permissions;
|
||||
in >> node._isReplicated;
|
||||
in >> node._localID;
|
||||
|
|
|
@ -147,7 +147,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
|
|||
|
||||
auto& packetReceiver = getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::DomainList,
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerList));
|
||||
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainList));
|
||||
packetReceiver.registerListener(PacketType::Ping,
|
||||
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingPacket));
|
||||
packetReceiver.registerListener(PacketType::PingReply,
|
||||
|
@ -357,7 +357,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
if (publicSockAddr.isNull()) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
qCDebug(networking_ice) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
|
||||
qCDebug(networking_ice) << "Waiting for initial public socket from STUN. Will not send domain-server check in.";
|
||||
} else if (domainHandlerIp.isNull() && _domainHandler.requiresICE()) {
|
||||
qCDebug(networking_ice) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
|
||||
handleICEConnectionToDomainServer();
|
||||
|
@ -401,6 +401,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
return;
|
||||
}
|
||||
|
||||
// WEBRTC TODO: Move code into packet library. And update reference in DomainConnectRequest.js.
|
||||
|
||||
auto domainPacket = NLPacket::create(domainPacketType);
|
||||
|
||||
QDataStream packetStream(domainPacket.get());
|
||||
|
@ -409,7 +411,6 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (domainPacketType == PacketType::DomainConnectRequest) {
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
// #######
|
||||
if (_shouldSendNewerVersion) {
|
||||
domainPacket->setVersion(versionForPacketType(domainPacketType) + 1);
|
||||
}
|
||||
|
@ -453,7 +454,6 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
packetStream << hardwareAddress;
|
||||
|
||||
// now add the machine fingerprint
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
packetStream << FingerprintUtils::getMachineFingerprint();
|
||||
|
||||
platform::json all = platform::getAll();
|
||||
|
@ -470,10 +470,12 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
QByteArray compressedSystemInfo = qCompress(systemInfo);
|
||||
|
||||
if (compressedSystemInfo.size() > MAX_SYSTEM_INFO_SIZE) {
|
||||
// FIXME
|
||||
// Highly unlikely, as not even unreasonable machines will
|
||||
// overflow the max size, but prevent MTU overflow anyway.
|
||||
// We could do something sophisticated like clearing specific
|
||||
// values if they're too big, but we'll save that for later.
|
||||
// Alternative solution would be to write system info at the end of the packet, only if there is space.
|
||||
compressedSystemInfo.clear();
|
||||
}
|
||||
|
||||
|
@ -494,7 +496,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
// pack our data to send to the domain-server including
|
||||
// the hostname information (so the domain-server can see which place name we came in on)
|
||||
packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList();
|
||||
packetStream << _ownerType.load() << publicSockAddr.getType() << publicSockAddr << localSockAddr.getType()
|
||||
<< localSockAddr << _nodeTypesOfInterest.toList();
|
||||
packetStream << DependencyManager::get<AddressManager>()->getPlaceName();
|
||||
|
||||
if (!domainIsConnected) {
|
||||
|
@ -711,7 +714,9 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<ReceivedM
|
|||
sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message) {
|
||||
void NodeList::processDomainList(QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
// WEBRTC TODO: Move code into packet library. And update reference in DomainServerList.js.
|
||||
|
||||
// parse header information
|
||||
QDataStream packetStream(message->getMessage());
|
||||
|
@ -877,14 +882,19 @@ void NodeList::processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> me
|
|||
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||
NewNodeInfo info;
|
||||
|
||||
SocketType publicSocketType, localSocketType;
|
||||
packetStream >> info.type
|
||||
>> info.uuid
|
||||
>> publicSocketType
|
||||
>> info.publicSocket
|
||||
>> localSocketType
|
||||
>> info.localSocket
|
||||
>> info.permissions
|
||||
>> info.isReplicated
|
||||
>> info.sessionLocalID
|
||||
>> info.connectionSecretUUID;
|
||||
info.publicSocket.setType(publicSocketType);
|
||||
info.localSocket.setType(localSocketType);
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
|
|
|
@ -112,7 +112,7 @@ public slots:
|
|||
void sendDomainServerCheckIn();
|
||||
void handleDSPathQuery(const QString& newPath);
|
||||
|
||||
void processDomainServerList(QSharedPointer<ReceivedMessage> message);
|
||||
void processDomainList(QSharedPointer<ReceivedMessage> message);
|
||||
void processDomainServerAddedNode(QSharedPointer<ReceivedMessage> message);
|
||||
void processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> message);
|
||||
void processDomainServerPathResponse(QSharedPointer<ReceivedMessage> message);
|
||||
|
@ -157,7 +157,9 @@ private slots:
|
|||
|
||||
private:
|
||||
Q_DISABLE_COPY(NodeList)
|
||||
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
||||
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) {
|
||||
assert(false); // Not implemented, needed for DependencyManager templates compile
|
||||
}
|
||||
NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 05/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -14,6 +15,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
/// @file
|
||||
/// @brief NodeType
|
||||
|
||||
/// @brief An 8-bit value identifying the type of a node - domain server, audio mixer, etc.
|
||||
typedef quint8 NodeType_t;
|
||||
|
||||
namespace NodeType {
|
||||
|
@ -37,8 +42,8 @@ namespace NodeType {
|
|||
NodeType_t upstreamType(NodeType_t primaryType);
|
||||
NodeType_t downstreamType(NodeType_t primaryType);
|
||||
|
||||
|
||||
NodeType_t fromString(QString type);
|
||||
NodeType_t fromChar(QChar type);
|
||||
}
|
||||
|
||||
typedef QSet<NodeType_t> NodeSet;
|
||||
|
|
|
@ -140,9 +140,7 @@ void PacketReceiver::handleVerifiedPacket(std::unique_ptr<udt::Packet> packet) {
|
|||
if (_shouldDropPackets) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
|
||||
// setup an NLPacket from the packet we were passed
|
||||
auto nlPacket = NLPacket::fromBase(std::move(packet));
|
||||
auto receivedMessage = QSharedPointer<ReceivedMessage>::create(*nlPacket);
|
||||
|
@ -163,7 +161,7 @@ void PacketReceiver::handleVerifiedMessagePacket(std::unique_ptr<udt::Packet> pa
|
|||
if (!message->isComplete()) {
|
||||
_pendingMessages[key] = message;
|
||||
}
|
||||
handleVerifiedMessage(message, true);
|
||||
handleVerifiedMessage(message, true); // Handler may handle first message packet immediately when it arrives.
|
||||
} else {
|
||||
message = it->second;
|
||||
message->appendPacket(*nlPacket);
|
||||
|
|
|
@ -28,21 +28,22 @@
|
|||
int sockAddrMetaTypeId = qRegisterMetaType<SockAddr>();
|
||||
|
||||
SockAddr::SockAddr() :
|
||||
_socketType(SocketType::Unknown),
|
||||
_address(),
|
||||
_port(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(const QHostAddress& address, quint16 port) :
|
||||
SockAddr::SockAddr(SocketType socketType, const QHostAddress& address, quint16 port) :
|
||||
_socketType(socketType),
|
||||
_address(address),
|
||||
_port(port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(const SockAddr& otherSockAddr) :
|
||||
QObject(),
|
||||
_socketType(otherSockAddr._socketType),
|
||||
_address(otherSockAddr._address),
|
||||
_port(otherSockAddr._port)
|
||||
{
|
||||
|
@ -51,12 +52,14 @@ SockAddr::SockAddr(const SockAddr& otherSockAddr) :
|
|||
|
||||
SockAddr& SockAddr::operator=(const SockAddr& rhsSockAddr) {
|
||||
setObjectName(rhsSockAddr.objectName());
|
||||
_socketType = rhsSockAddr._socketType;
|
||||
_address = rhsSockAddr._address;
|
||||
_port = rhsSockAddr._port;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
|
||||
SockAddr::SockAddr(SocketType socketType, const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
|
||||
_socketType(socketType),
|
||||
_address(hostname),
|
||||
_port(hostOrderPort)
|
||||
{
|
||||
|
@ -74,19 +77,10 @@ SockAddr::SockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBl
|
|||
}
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(const sockaddr* sockaddr) {
|
||||
_address = QHostAddress(sockaddr);
|
||||
|
||||
if (sockaddr->sa_family == AF_INET) {
|
||||
_port = ntohs(reinterpret_cast<const sockaddr_in*>(sockaddr)->sin_port);
|
||||
} else {
|
||||
_port = ntohs(reinterpret_cast<const sockaddr_in6*>(sockaddr)->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
void SockAddr::swap(SockAddr& otherSockAddr) {
|
||||
using std::swap;
|
||||
|
||||
|
||||
swap(_socketType, otherSockAddr._socketType);
|
||||
swap(_address, otherSockAddr._address);
|
||||
swap(_port, otherSockAddr._port);
|
||||
|
||||
|
@ -97,7 +91,7 @@ void SockAddr::swap(SockAddr& otherSockAddr) {
|
|||
}
|
||||
|
||||
bool SockAddr::operator==(const SockAddr& rhsSockAddr) const {
|
||||
return _address == rhsSockAddr._address && _port == rhsSockAddr._port;
|
||||
return _socketType == rhsSockAddr._socketType && _address == rhsSockAddr._address && _port == rhsSockAddr._port;
|
||||
}
|
||||
|
||||
void SockAddr::handleLookupResult(const QHostInfo& hostInfo) {
|
||||
|
@ -119,6 +113,10 @@ void SockAddr::handleLookupResult(const QHostInfo& hostInfo) {
|
|||
}
|
||||
|
||||
QString SockAddr::toString() const {
|
||||
return socketTypeToString(_socketType) + " " + _address.toString() + ":" + QString::number(_port);
|
||||
}
|
||||
|
||||
QString SockAddr::toShortString() const {
|
||||
return _address.toString() + ":" + QString::number(_port);
|
||||
}
|
||||
|
||||
|
@ -135,16 +133,21 @@ bool SockAddr::hasPrivateAddress() const {
|
|||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const SockAddr& sockAddr) {
|
||||
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||
debug.nospace()
|
||||
<< (sockAddr._socketType != SocketType::Unknown
|
||||
? (socketTypeToString(sockAddr._socketType) + " ").toLocal8Bit().constData() : "")
|
||||
<< sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& dataStream, const SockAddr& sockAddr) {
|
||||
// Don't include socket type because ICE packets must not have it.
|
||||
dataStream << sockAddr._address << sockAddr._port;
|
||||
return dataStream;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& dataStream, SockAddr& sockAddr) {
|
||||
// Don't include socket type because ICE packets must not have it.
|
||||
dataStream >> sockAddr._address >> sockAddr._port;
|
||||
return dataStream;
|
||||
}
|
||||
|
|
|
@ -20,14 +20,16 @@ struct sockaddr;
|
|||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "SocketType.h"
|
||||
|
||||
|
||||
class SockAddr : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SockAddr();
|
||||
SockAddr(const QHostAddress& address, quint16 port);
|
||||
SockAddr(SocketType socketType, const QHostAddress& address, quint16 port);
|
||||
SockAddr(const SockAddr& otherSockAddr);
|
||||
SockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
||||
SockAddr(const sockaddr* sockaddr);
|
||||
SockAddr(SocketType socketType, const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
||||
|
||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||
void clear() { _address.clear(); _port = 0;}
|
||||
|
@ -38,6 +40,10 @@ public:
|
|||
bool operator==(const SockAddr& rhsSockAddr) const;
|
||||
bool operator!=(const SockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
|
||||
|
||||
SocketType getType() const { return _socketType; }
|
||||
SocketType* getSocketTypePointer() { return &_socketType; }
|
||||
void setType(const SocketType socketType) { _socketType = socketType; }
|
||||
|
||||
const QHostAddress& getAddress() const { return _address; }
|
||||
QHostAddress* getAddressPointer() { return &_address; }
|
||||
void setAddress(const QHostAddress& address) { _address = address; }
|
||||
|
@ -50,19 +56,21 @@ public:
|
|||
static int unpackSockAddr(const unsigned char* packetData, SockAddr& unpackDestSockAddr);
|
||||
|
||||
QString toString() const;
|
||||
QString toShortString() const;
|
||||
|
||||
bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918
|
||||
|
||||
friend QDebug operator<<(QDebug debug, const SockAddr& sockAddr);
|
||||
friend QDataStream& operator<<(QDataStream& dataStream, const SockAddr& sockAddr);
|
||||
friend QDataStream& operator>>(QDataStream& dataStream, SockAddr& sockAddr);
|
||||
|
||||
|
||||
private slots:
|
||||
void handleLookupResult(const QHostInfo& hostInfo);
|
||||
signals:
|
||||
void lookupCompleted();
|
||||
void lookupFailed();
|
||||
private:
|
||||
SocketType _socketType { SocketType::Unknown };
|
||||
QHostAddress _address;
|
||||
quint16 _port;
|
||||
};
|
||||
|
|
39
libraries/networking/src/SocketType.h
Normal file
39
libraries/networking/src/SocketType.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// SocketType.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by David Rowe on 17 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Handles UDP and WebRTC sockets in parallel.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef vircadia_SocketType_h
|
||||
#define vircadia_SocketType_h
|
||||
|
||||
/// @addtogroup Networking
|
||||
/// @{
|
||||
|
||||
|
||||
/// @brief The types of network socket.
|
||||
enum class SocketType : uint8_t {
|
||||
Unknown, ///< Socket type unknown or not set.
|
||||
UDP, ///< UDP socket.
|
||||
WebRTC ///< WebRTC socket. A WebRTC data channel presented as a UDP-style socket.
|
||||
};
|
||||
|
||||
/// @brief Returns the name of a SocketType value, e.g., <code>"WebRTC"</code>.
|
||||
/// @param socketType The SocketType value.
|
||||
/// @return The name of the SocketType value.
|
||||
static QString socketTypeToString(SocketType socketType) {
|
||||
static QStringList SOCKET_TYPE_STRINGS { "Unknown", "UDP", "WebRTC" };
|
||||
return SOCKET_TYPE_STRINGS[(int)socketType];
|
||||
}
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // vircadia_SocketType_h
|
|
@ -58,7 +58,7 @@ BasePacket::BasePacket(qint64 size) {
|
|||
}
|
||||
|
||||
// Sanity check
|
||||
Q_ASSERT(size >= 0 || size < maxPayload);
|
||||
Q_ASSERT(size >= 0 && size <= maxPayload);
|
||||
|
||||
_packetSize = size;
|
||||
_packet.reset(new char[_packetSize]());
|
||||
|
|
|
@ -54,7 +54,7 @@ Connection::Connection(Socket* parentSocket, SockAddr destination, std::unique_p
|
|||
static std::mt19937 generator(rd());
|
||||
static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX);
|
||||
|
||||
// randomize the intial sequence number
|
||||
// randomize the initial sequence number
|
||||
_initialSequenceNumber = SequenceNumber(distribution(generator));
|
||||
}
|
||||
|
||||
|
@ -255,9 +255,6 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
|
|||
return false;
|
||||
}
|
||||
|
||||
// mark our last receive time as now (to push the potential expiry farther)
|
||||
_lastReceiveTime = p_high_resolution_clock::now();
|
||||
|
||||
// If this is not the next sequence number, report loss
|
||||
if (sequenceNumber > _lastReceivedSequenceNumber + 1) {
|
||||
if (_lastReceivedSequenceNumber + 1 == sequenceNumber - 1) {
|
||||
|
@ -417,9 +414,6 @@ void Connection::resetReceiveState() {
|
|||
// clear the loss list
|
||||
_lossList.clear();
|
||||
|
||||
// clear sync variables
|
||||
_connectionStart = p_high_resolution_clock::now();
|
||||
|
||||
// clear any pending received messages
|
||||
for (auto& pendingMessage : _pendingReceivedMessages) {
|
||||
_parentSocket->messageFailed(this, pendingMessage.first);
|
||||
|
@ -451,12 +445,6 @@ void PendingReceivedMessage::enqueuePacket(std::unique_ptr<Packet> packet) {
|
|||
"PendingReceivedMessage::enqueuePacket",
|
||||
"called with a packet that is not part of a message");
|
||||
|
||||
if (packet->getPacketPosition() == Packet::PacketPosition::LAST ||
|
||||
packet->getPacketPosition() == Packet::PacketPosition::ONLY) {
|
||||
_hasLastPacket = true;
|
||||
_numPackets = packet->getMessagePartNumber() + 1;
|
||||
}
|
||||
|
||||
// Insert into the packets list in sorted order. Because we generally expect to receive packets in order, begin
|
||||
// searching from the end of the list.
|
||||
auto messagePartNumber = packet->getMessagePartNumber();
|
||||
|
|
|
@ -43,9 +43,7 @@ public:
|
|||
std::list<std::unique_ptr<Packet>> _packets;
|
||||
|
||||
private:
|
||||
bool _hasLastPacket { false };
|
||||
Packet::MessagePartNumber _nextPartNumber = 0;
|
||||
unsigned int _numPackets { 0 };
|
||||
};
|
||||
|
||||
class Connection : public QObject {
|
||||
|
@ -112,9 +110,6 @@ private:
|
|||
bool _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
|
||||
bool _didRequestHandshake { false }; // flag for request of handshake from server
|
||||
|
||||
p_high_resolution_clock::time_point _connectionStart = p_high_resolution_clock::now(); // holds the time_point for creation of this connection
|
||||
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
|
||||
|
||||
SequenceNumber _initialSequenceNumber; // Randomized on Connection creation, identifies connection during re-connect requests
|
||||
SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer Connection on creation, identifies connection during re-connect requests
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace udt {
|
|||
static const int CONNECTION_SEND_BUFFER_SIZE_PACKETS = 8192;
|
||||
static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576;
|
||||
static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
|
||||
static const int WEBRTC_SEND_BUFFER_SIZE_BYTES = 1048576;
|
||||
static const int WEBRTC_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
|
||||
static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000;
|
||||
|
||||
|
||||
|
|
|
@ -98,10 +98,11 @@ void ControlPacket::writeType() {
|
|||
void ControlPacket::readType() {
|
||||
ControlBitAndType bitAndType = *reinterpret_cast<ControlBitAndType*>(_packet.get());
|
||||
|
||||
Q_ASSERT_X(bitAndType & CONTROL_BIT_MASK, "ControlPacket::readHeader()", "This should be a control packet");
|
||||
Q_ASSERT_X(bitAndType & CONTROL_BIT_MASK, "ControlPacket::readType()", "This should be a control packet");
|
||||
|
||||
uint16_t packetType = (bitAndType & ~CONTROL_BIT_MASK) >> (8 * sizeof(Type));
|
||||
Q_ASSERT_X(packetType <= ControlPacket::Type::HandshakeRequest, "ControlPacket::readType()", "Received a control packet with wrong type");
|
||||
Q_ASSERT_X(packetType <= ControlPacket::Type::HandshakeRequest, "ControlPacket::readType()",
|
||||
"Received a control packet with invalid type");
|
||||
|
||||
// read the type
|
||||
_type = (Type) packetType;
|
||||
|
|
291
libraries/networking/src/udt/NetworkSocket.cpp
Normal file
291
libraries/networking/src/udt/NetworkSocket.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
//
|
||||
// NetworkSocket.cpp
|
||||
// libraries/networking/src/udt
|
||||
//
|
||||
// Created by David Rowe on 21 Jun 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#include "NetworkSocket.h"
|
||||
|
||||
#include "../NetworkLogging.h"
|
||||
|
||||
|
||||
NetworkSocket::NetworkSocket(QObject* parent) :
|
||||
QObject(parent),
|
||||
_parent(parent),
|
||||
_udpSocket(this)
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
,
|
||||
_webrtcSocket(this)
|
||||
#endif
|
||||
{
|
||||
connect(&_udpSocket, &QUdpSocket::readyRead, this, &NetworkSocket::readyRead);
|
||||
connect(&_udpSocket, &QAbstractSocket::stateChanged, this, &NetworkSocket::onUDPStateChanged);
|
||||
// Use old SIGNAL/SLOT mechanism for Android builds.
|
||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
this, SLOT(onUDPSocketError(QAbstractSocket::SocketError)));
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
connect(&_webrtcSocket, &WebRTCSocket::readyRead, this, &NetworkSocket::readyRead);
|
||||
connect(&_webrtcSocket, &WebRTCSocket::stateChanged, this, &NetworkSocket::onWebRTCStateChanged);
|
||||
// WEBRTC TODO: Add similar for errorOccurred
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void NetworkSocket::setSocketOption(SocketType socketType, QAbstractSocket::SocketOption option, const QVariant& value) {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
_udpSocket.setSocketOption(option, value);
|
||||
break;
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
_webrtcSocket.setSocketOption(option, value);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in setSocketOption()";
|
||||
}
|
||||
}
|
||||
|
||||
QVariant NetworkSocket::socketOption(SocketType socketType, QAbstractSocket::SocketOption option) {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.socketOption(option);
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.socketOption(option);
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in socketOption()";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NetworkSocket::bind(SocketType socketType, const QHostAddress& address, quint16 port) {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
_udpSocket.bind(address, port);
|
||||
break;
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
_webrtcSocket.bind(address, port);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in bind()";
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkSocket::abort(SocketType socketType) {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
_udpSocket.abort();
|
||||
break;
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
_webrtcSocket.abort();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in abort()";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
quint16 NetworkSocket::localPort(SocketType socketType) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.localPort();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.localPort();
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in localPort()";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qintptr NetworkSocket::socketDescriptor(SocketType socketType) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.socketDescriptor();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.socketDescriptor();
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in socketDescriptor()";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr) {
|
||||
switch (sockAddr.getType()) {
|
||||
case SocketType::UDP:
|
||||
// WEBRTC TODO: The Qt documentation says that the following call shouldn't be used if the UDP socket is connected!!!
|
||||
// https://doc.qt.io/qt-5/qudpsocket.html#writeDatagram
|
||||
return _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.writeDatagram(datagram, sockAddr);
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in writeDatagram() address";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 NetworkSocket::bytesToWrite(SocketType socketType, const SockAddr& address) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.bytesToWrite();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.bytesToWrite(address);
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in bytesToWrite()";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NetworkSocket::hasPendingDatagrams() const {
|
||||
return
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
_webrtcSocket.hasPendingDatagrams() ||
|
||||
#endif
|
||||
_udpSocket.hasPendingDatagrams();
|
||||
}
|
||||
|
||||
qint64 NetworkSocket::pendingDatagramSize() {
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
// Alternate socket types, remembering the socket type used so that the same socket type is used next readDatagram().
|
||||
if (_lastSocketTypeRead == SocketType::UDP) {
|
||||
if (_webrtcSocket.hasPendingDatagrams()) {
|
||||
_pendingDatagramSizeSocketType = SocketType::WebRTC;
|
||||
return _webrtcSocket.pendingDatagramSize();
|
||||
} else {
|
||||
_pendingDatagramSizeSocketType = SocketType::UDP;
|
||||
return _udpSocket.pendingDatagramSize();
|
||||
}
|
||||
} else {
|
||||
if (_udpSocket.hasPendingDatagrams()) {
|
||||
_pendingDatagramSizeSocketType = SocketType::UDP;
|
||||
return _udpSocket.pendingDatagramSize();
|
||||
} else {
|
||||
_pendingDatagramSizeSocketType = SocketType::WebRTC;
|
||||
return _webrtcSocket.pendingDatagramSize();
|
||||
}
|
||||
}
|
||||
#else
|
||||
return _udpSocket.pendingDatagramSize();
|
||||
#endif
|
||||
}
|
||||
|
||||
qint64 NetworkSocket::readDatagram(char* data, qint64 maxSize, SockAddr* sockAddr) {
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
// Read per preceding pendingDatagramSize() if any, otherwise alternate socket types.
|
||||
if (_pendingDatagramSizeSocketType == SocketType::UDP
|
||||
|| _pendingDatagramSizeSocketType == SocketType::Unknown && _lastSocketTypeRead == SocketType::WebRTC) {
|
||||
_lastSocketTypeRead = SocketType::UDP;
|
||||
_pendingDatagramSizeSocketType = SocketType::Unknown;
|
||||
if (sockAddr) {
|
||||
sockAddr->setType(SocketType::UDP);
|
||||
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _udpSocket.readDatagram(data, maxSize);
|
||||
}
|
||||
} else {
|
||||
_lastSocketTypeRead = SocketType::WebRTC;
|
||||
_pendingDatagramSizeSocketType = SocketType::Unknown;
|
||||
if (sockAddr) {
|
||||
sockAddr->setType(SocketType::WebRTC);
|
||||
return _webrtcSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _webrtcSocket.readDatagram(data, maxSize);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (sockAddr) {
|
||||
sockAddr->setType(SocketType::UDP);
|
||||
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _udpSocket.readDatagram(data, maxSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QAbstractSocket::SocketState NetworkSocket::state(SocketType socketType) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.state();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.state();
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in state()";
|
||||
return QAbstractSocket::SocketState::UnconnectedState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QAbstractSocket::SocketError NetworkSocket::error(SocketType socketType) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.error();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.error();
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in error()";
|
||||
return QAbstractSocket::SocketError::UnknownSocketError;
|
||||
}
|
||||
}
|
||||
|
||||
QString NetworkSocket::errorString(SocketType socketType) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.errorString();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.errorString();
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in errorString()";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const WebRTCSocket* NetworkSocket::getWebRTCSocket() {
|
||||
return &_webrtcSocket;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void NetworkSocket::onUDPStateChanged(QAbstractSocket::SocketState socketState) {
|
||||
emit stateChanged(SocketType::UDP, socketState);
|
||||
}
|
||||
|
||||
void NetworkSocket::onWebRTCStateChanged(QAbstractSocket::SocketState socketState) {
|
||||
emit stateChanged(SocketType::WebRTC, socketState);
|
||||
}
|
||||
|
||||
void NetworkSocket::onUDPSocketError(QAbstractSocket::SocketError socketError) {
|
||||
emit NetworkSocket::socketError(SocketType::UDP, socketError);
|
||||
}
|
||||
|
||||
void NetworkSocket::onWebRTCSocketError(QAbstractSocket::SocketError socketError) {
|
||||
emit NetworkSocket::socketError(SocketType::WebRTC, socketError);
|
||||
}
|
170
libraries/networking/src/udt/NetworkSocket.h
Normal file
170
libraries/networking/src/udt/NetworkSocket.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
//
|
||||
// NetworkSocket.h
|
||||
// libraries/networking/src/udt
|
||||
//
|
||||
// Created by David Rowe on 21 Jun 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#ifndef vircadia_NetworkSocket_h
|
||||
#define vircadia_NetworkSocket_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#include "../SockAddr.h"
|
||||
#include "../NodeType.h"
|
||||
#include "../SocketType.h"
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
#include "../webrtc/WebRTCSocket.h"
|
||||
#endif
|
||||
|
||||
/// @addtogroup Networking
|
||||
/// @{
|
||||
|
||||
|
||||
/// @brief Multiplexes a QUdpSocket and a WebRTCSocket so that they appear as a single QUdpSocket-style socket.
|
||||
class NetworkSocket : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new NetworkSocket object.
|
||||
/// @param parent Qt parent object.
|
||||
NetworkSocket(QObject* parent);
|
||||
|
||||
|
||||
/// @brief Set the value of a UDP or WebRTC socket option.
|
||||
/// @param socketType The type of socket for which to set the option value.
|
||||
/// @param option The option to set the value of.
|
||||
/// @param value The option value.
|
||||
void setSocketOption(SocketType socketType, QAbstractSocket::SocketOption option, const QVariant& value);
|
||||
|
||||
/// @brief Gets the value of a UDP or WebRTC socket option.
|
||||
/// @param socketType The type of socket for which to get the option value.
|
||||
/// @param option The option to get the value of.
|
||||
/// @return The option value.
|
||||
QVariant socketOption(SocketType socketType, QAbstractSocket::SocketOption option);
|
||||
|
||||
|
||||
/// @brief Binds the UDP or WebRTC socket to an address and port.
|
||||
/// @param socketType The type of socket to bind.
|
||||
/// @param address The address to bind to.
|
||||
/// @param port The port to bind to.
|
||||
void bind(SocketType socketType, const QHostAddress& address, quint16 port = 0);
|
||||
|
||||
/// @brief Immediately closes and resets the socket.
|
||||
/// @param socketType The type of socket to close and reset.
|
||||
void abort(SocketType socketType);
|
||||
|
||||
|
||||
/// @brief Gets the UDP or WebRTC local port number.
|
||||
/// @param socketType The type of socket for which to the get local port number.
|
||||
/// @return The UDP or WebRTC local port number if available, otherwise <code>0</code>.
|
||||
quint16 localPort(SocketType socketType) const;
|
||||
|
||||
/// @brief Returns the native socket descriptor of the UDP or WebRTC socket.
|
||||
/// @param socketType The type of socket to get the socket descriptor for.
|
||||
/// @return The native socket descriptor if available, otherwise <code>-1</code>.
|
||||
qintptr socketDescriptor(SocketType socketType) const;
|
||||
|
||||
|
||||
/// @brief Sends a datagram to a network address.
|
||||
/// @param datagram The datagram to send.
|
||||
/// @param sockAddr The address to send to.
|
||||
/// @return The number of bytes if successfully sent, otherwise <code>-1</code>.
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be written.
|
||||
/// @details For UDP, there's a single buffer used for all destinations. For WebRTC, each destination has its own buffer.
|
||||
/// @param socketType The type of socket for which to get the number of bytes waiting to be written.
|
||||
/// @param address If a WebRTCSocket, the destination address for which to get the number of bytes waiting.
|
||||
/// @param port If a WebRTC socket, the destination port for which to get the number of bytes waiting.
|
||||
/// @return The number of bytes waiting to be written.
|
||||
qint64 bytesToWrite(SocketType socketType, const SockAddr& address = SockAddr()) const;
|
||||
|
||||
|
||||
/// @brief Gets whether there is a pending datagram waiting to be read.
|
||||
/// @return <code>true</code> if there is a datagram waiting to be read, <code>false</code> if there isn't.
|
||||
bool hasPendingDatagrams() const;
|
||||
|
||||
/// @brief Gets the size of the next pending datagram, alternating between socket types if both have datagrams to read.
|
||||
/// @return The size of the next pending datagram.
|
||||
qint64 pendingDatagramSize();
|
||||
|
||||
/// @brief Reads the next datagram per the most recent pendingDatagramSize call if made, otherwise alternating between
|
||||
/// socket types if both have datagrams to read.
|
||||
/// @param data The destination to write the data into.
|
||||
/// @param maxSize The maximum number of bytes to read.
|
||||
/// @param sockAddr The destination to write the source network address into.
|
||||
/// @return The number of bytes if successfully read, otherwise <code>-1</code>.
|
||||
qint64 readDatagram(char* data, qint64 maxSize, SockAddr* sockAddr = nullptr);
|
||||
|
||||
|
||||
/// @brief Gets the state of the UDP or WebRTC socket.
|
||||
/// @param socketType The type of socket for which to get the state.
|
||||
/// @return The socket state.
|
||||
QAbstractSocket::SocketState state(SocketType socketType) const;
|
||||
|
||||
|
||||
/// @brief Gets the type of error that last occurred.
|
||||
/// @param socketType The type of socket for which to get the last error.
|
||||
/// @return The type of error that last occurred.
|
||||
QAbstractSocket::SocketError error(SocketType socketType) const;
|
||||
|
||||
/// @brief Gets the description of the error that last occurred.
|
||||
/// @param socketType The type of socket for which to get the last error's description.
|
||||
/// @return The description of the error that last occurred.
|
||||
QString errorString(SocketType socketType) const;
|
||||
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
/// @brief Gets a pointer to the WebRTC socket object.
|
||||
/// @return A pointer to the WebRTC socket object.
|
||||
const WebRTCSocket* getWebRTCSocket();
|
||||
#endif
|
||||
|
||||
signals:
|
||||
|
||||
/// @brief Emitted each time new data becomes available for reading.
|
||||
void readyRead();
|
||||
|
||||
/// @brief Emitted when the state of the underlying UDP or WebRTC socket changes.
|
||||
/// @param socketType The type of socket that changed state.
|
||||
/// @param socketState The socket's new state.
|
||||
void stateChanged(SocketType socketType, QAbstractSocket::SocketState socketState);
|
||||
|
||||
/// @brief
|
||||
/// @param socketType
|
||||
/// @param socketError
|
||||
void socketError(SocketType socketType, QAbstractSocket::SocketError socketError);
|
||||
|
||||
private slots:
|
||||
|
||||
void onUDPStateChanged(QAbstractSocket::SocketState socketState);
|
||||
void onWebRTCStateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
void onUDPSocketError(QAbstractSocket::SocketError socketError);
|
||||
void onWebRTCSocketError(QAbstractSocket::SocketError socketError);
|
||||
|
||||
private:
|
||||
|
||||
QObject* _parent;
|
||||
|
||||
QUdpSocket _udpSocket;
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
WebRTCSocket _webrtcSocket;
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
SocketType _pendingDatagramSizeSocketType { SocketType::Unknown };
|
||||
SocketType _lastSocketTypeRead { SocketType::Unknown };
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // vircadia_NetworkSocket_h
|
|
@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::DomainConnectRequestPending: // keeping the old version to maintain the protocol hash
|
||||
return 17;
|
||||
case PacketType::DomainList:
|
||||
return static_cast<PacketVersion>(DomainListVersion::HasConnectReason);
|
||||
return static_cast<PacketVersion>(DomainListVersion::SocketTypes);
|
||||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityClone:
|
||||
case PacketType::EntityEdit:
|
||||
|
@ -72,10 +72,12 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
|
||||
|
||||
case PacketType::DomainConnectRequest:
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasCompressedSystemInfo);
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::SocketTypes);
|
||||
case PacketType::DomainListRequest:
|
||||
return static_cast<PacketVersion>(DomainListRequestVersion::SocketTypes);
|
||||
|
||||
case PacketType::DomainServerAddedNode:
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::SocketTypes);
|
||||
|
||||
case PacketType::EntityScriptCallMethod:
|
||||
return static_cast<PacketVersion>(EntityScriptCallMethodVersion::ClientCallable);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// WEBRTC TODO: Rename / split up into files with better names.
|
||||
|
||||
#ifndef hifi_PacketHeaders_h
|
||||
#define hifi_PacketHeaders_h
|
||||
|
||||
|
@ -137,6 +139,7 @@ public:
|
|||
BulkAvatarTraitsAck,
|
||||
StopInjector,
|
||||
AvatarZonePresence,
|
||||
WebRTCSignaling,
|
||||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
|
@ -188,7 +191,7 @@ public:
|
|||
<< PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio
|
||||
<< PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity
|
||||
<< PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData
|
||||
<< PacketTypeEnum::Value::AvatarZonePresence;
|
||||
<< PacketTypeEnum::Value::AvatarZonePresence << PacketTypeEnum::Value::WebRTCSignaling;
|
||||
return NON_SOURCED_PACKETS;
|
||||
}
|
||||
|
||||
|
@ -365,7 +368,13 @@ enum class DomainConnectRequestVersion : PacketVersion {
|
|||
HasTimestamp,
|
||||
HasReason,
|
||||
HasSystemInfo,
|
||||
HasCompressedSystemInfo
|
||||
HasCompressedSystemInfo,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainListRequestVersion : PacketVersion {
|
||||
PreSocketTypes = 22,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainConnectionDeniedVersion : PacketVersion {
|
||||
|
@ -376,7 +385,8 @@ enum class DomainConnectionDeniedVersion : PacketVersion {
|
|||
|
||||
enum class DomainServerAddedNodeVersion : PacketVersion {
|
||||
PrePermissionsGrid = 17,
|
||||
PermissionsGrid
|
||||
PermissionsGrid,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainListVersion : PacketVersion {
|
||||
|
@ -386,7 +396,8 @@ enum class DomainListVersion : PacketVersion {
|
|||
GetMachineFingerprintFromUUIDSupport,
|
||||
AuthenticationOptional,
|
||||
HasTimestamp,
|
||||
HasConnectReason
|
||||
HasConnectReason,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class AudioVersion : PacketVersion {
|
||||
|
|
|
@ -25,8 +25,8 @@ public:
|
|||
using UType = uint32_t;
|
||||
|
||||
// Values are for 27 bit SequenceNumber
|
||||
static const Type THRESHOLD = 0x03FFFFFF; // threshold for comparing sequence numbers
|
||||
static const Type MAX = 0x07FFFFFF; // maximum sequence number used in UDT
|
||||
static const Type THRESHOLD = 0x03FFFFFF; // Threshold for comparing sequence numbers.
|
||||
static const Type MAX = 0x07FFFFFF; // Maximum sequence number used in UDT.
|
||||
|
||||
SequenceNumber() = default;
|
||||
SequenceNumber(const SequenceNumber& other) : _value(other._value) {}
|
||||
|
|
|
@ -42,16 +42,15 @@ using namespace udt;
|
|||
|
||||
Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
|
||||
QObject(parent),
|
||||
_udpSocket(parent),
|
||||
_networkSocket(parent),
|
||||
_readyReadBackupTimer(new QTimer(this)),
|
||||
_shouldChangeSocketOptions(shouldChangeSocketOptions)
|
||||
{
|
||||
connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams);
|
||||
connect(&_networkSocket, &NetworkSocket::readyRead, this, &Socket::readPendingDatagrams);
|
||||
|
||||
// make sure we hear about errors and state changes from the underlying socket
|
||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
this, SLOT(handleSocketError(QAbstractSocket::SocketError)));
|
||||
connect(&_udpSocket, &QAbstractSocket::stateChanged, this, &Socket::handleStateChanged);
|
||||
connect(&_networkSocket, &NetworkSocket::socketError, this, &Socket::handleSocketError);
|
||||
connect(&_networkSocket, &NetworkSocket::stateChanged, this, &Socket::handleStateChanged);
|
||||
|
||||
// in order to help track down the zombie server bug, add a timer to check if we missed a readyRead
|
||||
const int READY_READ_BACKUP_CHECK_MSECS = 2 * 1000;
|
||||
|
@ -59,19 +58,21 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
|
|||
_readyReadBackupTimer->start(READY_READ_BACKUP_CHECK_MSECS);
|
||||
}
|
||||
|
||||
void Socket::bind(const QHostAddress& address, quint16 port) {
|
||||
|
||||
_udpSocket.bind(address, port);
|
||||
void Socket::bind(SocketType socketType, const QHostAddress& address, quint16 port) {
|
||||
_networkSocket.bind(socketType, address, port);
|
||||
|
||||
if (_shouldChangeSocketOptions) {
|
||||
setSystemBufferSizes();
|
||||
setSystemBufferSizes(socketType);
|
||||
if (socketType == SocketType::WebRTC) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
auto sd = _udpSocket.socketDescriptor();
|
||||
auto sd = _networkSocket.socketDescriptor(socketType);
|
||||
int val = IP_PMTUDISC_DONT;
|
||||
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
|
||||
#elif defined(Q_OS_WIN)
|
||||
auto sd = _udpSocket.socketDescriptor();
|
||||
auto sd = _networkSocket.socketDescriptor(socketType);
|
||||
int val = 0; // false
|
||||
if (setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, (const char *)&val, sizeof(val))) {
|
||||
auto wsaErr = WSAGetLastError();
|
||||
|
@ -81,16 +82,22 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
|
|||
}
|
||||
}
|
||||
|
||||
void Socket::rebind() {
|
||||
rebind(_udpSocket.localPort());
|
||||
void Socket::rebind(SocketType socketType) {
|
||||
rebind(socketType, _networkSocket.localPort(socketType));
|
||||
}
|
||||
|
||||
void Socket::rebind(quint16 localPort) {
|
||||
_udpSocket.abort();
|
||||
bind(QHostAddress::AnyIPv4, localPort);
|
||||
void Socket::rebind(SocketType socketType, quint16 localPort) {
|
||||
_networkSocket.abort(socketType);
|
||||
bind(socketType, QHostAddress::AnyIPv4, localPort);
|
||||
}
|
||||
|
||||
void Socket::setSystemBufferSizes() {
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const WebRTCSocket* Socket::getWebRTCSocket() {
|
||||
return _networkSocket.getWebRTCSocket();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Socket::setSystemBufferSizes(SocketType socketType) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
QAbstractSocket::SocketOption bufferOpt;
|
||||
QString bufferTypeString;
|
||||
|
@ -99,20 +106,22 @@ void Socket::setSystemBufferSizes() {
|
|||
|
||||
if (i == 0) {
|
||||
bufferOpt = QAbstractSocket::SendBufferSizeSocketOption;
|
||||
numBytes = udt::UDP_SEND_BUFFER_SIZE_BYTES;
|
||||
numBytes = socketType == SocketType::UDP
|
||||
? udt::UDP_SEND_BUFFER_SIZE_BYTES : udt::WEBRTC_SEND_BUFFER_SIZE_BYTES;
|
||||
bufferTypeString = "send";
|
||||
|
||||
} else {
|
||||
bufferOpt = QAbstractSocket::ReceiveBufferSizeSocketOption;
|
||||
numBytes = udt::UDP_RECEIVE_BUFFER_SIZE_BYTES;
|
||||
numBytes = socketType == SocketType::UDP
|
||||
? udt::UDP_RECEIVE_BUFFER_SIZE_BYTES : udt::WEBRTC_RECEIVE_BUFFER_SIZE_BYTES;
|
||||
bufferTypeString = "receive";
|
||||
}
|
||||
|
||||
int oldBufferSize = _udpSocket.socketOption(bufferOpt).toInt();
|
||||
int oldBufferSize = _networkSocket.socketOption(socketType, bufferOpt).toInt();
|
||||
|
||||
if (oldBufferSize < numBytes) {
|
||||
_udpSocket.setSocketOption(bufferOpt, QVariant(numBytes));
|
||||
int newBufferSize = _udpSocket.socketOption(bufferOpt).toInt();
|
||||
_networkSocket.setSocketOption(socketType, bufferOpt, QVariant(numBytes));
|
||||
int newBufferSize = _networkSocket.socketOption(socketType, bufferOpt).toInt();
|
||||
|
||||
qCDebug(networking) << "Changed socket" << bufferTypeString << "buffer size from" << oldBufferSize << "to"
|
||||
<< newBufferSize << "bytes";
|
||||
|
@ -198,7 +207,7 @@ qint64 Socket::writePacketList(std::unique_ptr<PacketList> packetList, const Soc
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Unerliable and Unordered
|
||||
// Unreliable and Unordered
|
||||
qint64 totalBytesSent = 0;
|
||||
while (!packetList->_packets.empty()) {
|
||||
totalBytesSent += writePacket(packetList->takeFront<Packet>(), sockAddr);
|
||||
|
@ -236,16 +245,18 @@ qint64 Socket::writeDatagram(const char* data, qint64 size, const SockAddr& sock
|
|||
}
|
||||
|
||||
qint64 Socket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr) {
|
||||
auto socketType = sockAddr.getType();
|
||||
|
||||
// don't attempt to write the datagram if we're unbound. Just drop it.
|
||||
// _udpSocket.writeDatagram will return an error anyway, but there are
|
||||
// _networkSocket.writeDatagram will return an error anyway, but there are
|
||||
// potential crashes in Qt when that happens.
|
||||
if (_udpSocket.state() != QAbstractSocket::BoundState) {
|
||||
if (_networkSocket.state(socketType) != QAbstractSocket::BoundState) {
|
||||
qCDebug(networking) << "Attempt to writeDatagram when in unbound state to" << sockAddr;
|
||||
return -1;
|
||||
}
|
||||
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
|
||||
int pending = _udpSocket.bytesToWrite();
|
||||
qint64 bytesWritten = _networkSocket.writeDatagram(datagram, sockAddr);
|
||||
|
||||
int pending = _networkSocket.bytesToWrite(socketType, sockAddr);
|
||||
if (bytesWritten < 0 || pending) {
|
||||
int wsaError = 0;
|
||||
static std::atomic<int> previousWsaError (0);
|
||||
|
@ -253,8 +264,8 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAdd
|
|||
wsaError = WSAGetLastError();
|
||||
#endif
|
||||
QString errorString;
|
||||
QDebug(&errorString) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - "
|
||||
<< wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
|
||||
QDebug(&errorString) << "udt::writeDatagram (" << _networkSocket.state(socketType) << sockAddr << ") error - "
|
||||
<< wsaError << _networkSocket.error(socketType) << "(" << _networkSocket.errorString(socketType) << ")"
|
||||
<< (pending ? "pending bytes:" : "pending:") << pending;
|
||||
|
||||
if (previousWsaError.exchange(wsaError) != wsaError) {
|
||||
|
@ -344,7 +355,7 @@ void Socket::messageFailed(Connection* connection, Packet::MessageNumber message
|
|||
}
|
||||
|
||||
void Socket::checkForReadyReadBackup() {
|
||||
if (_udpSocket.hasPendingDatagrams()) {
|
||||
if (_networkSocket.hasPendingDatagrams()) {
|
||||
qCDebug(networking) << "Socket::checkForReadyReadBackup() detected blocked readyRead signal. Flushing pending datagrams.";
|
||||
|
||||
// so that birarda can possibly figure out how the heck we get into this state in the first place
|
||||
|
@ -358,8 +369,8 @@ void Socket::checkForReadyReadBackup() {
|
|||
|
||||
// drop all of the pending datagrams on the floor
|
||||
int droppedCount = 0;
|
||||
while (_udpSocket.hasPendingDatagrams()) {
|
||||
_udpSocket.readDatagram(nullptr, 0);
|
||||
while (_networkSocket.hasPendingDatagrams()) {
|
||||
_networkSocket.readDatagram(nullptr, 0);
|
||||
++droppedCount;
|
||||
}
|
||||
qCDebug(networking) << "Flushed" << droppedCount << "Packets";
|
||||
|
@ -372,8 +383,8 @@ void Socket::readPendingDatagrams() {
|
|||
const auto abortTime = system_clock::now() + MAX_PROCESS_TIME;
|
||||
int packetSizeWithHeader = -1;
|
||||
|
||||
while (_udpSocket.hasPendingDatagrams() &&
|
||||
(packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
|
||||
while (_networkSocket.hasPendingDatagrams() &&
|
||||
(packetSizeWithHeader = _networkSocket.pendingDatagramSize()) != -1) {
|
||||
if (system_clock::now() > abortTime) {
|
||||
// We've been running for too long, stop processing packets for now
|
||||
// Once we've processed the event queue, we'll come back to packet processing
|
||||
|
@ -398,8 +409,7 @@ void Socket::readPendingDatagrams() {
|
|||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||
|
||||
// pull the datagram
|
||||
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||
auto sizeRead = _networkSocket.readDatagram(buffer.get(), packetSizeWithHeader, &senderSockAddr);
|
||||
|
||||
// save information for this packet, in case it is the one that sticks readyRead
|
||||
_lastPacketSizeRead = sizeRead;
|
||||
|
@ -541,17 +551,17 @@ std::vector<SockAddr> Socket::getConnectionSockAddrs() {
|
|||
return addr;
|
||||
}
|
||||
|
||||
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
|
||||
void Socket::handleSocketError(SocketType socketType, QAbstractSocket::SocketError socketError) {
|
||||
int wsaError = 0;
|
||||
static std::atomic<int> previousWsaError(0);
|
||||
#ifdef WIN32
|
||||
wsaError = WSAGetLastError();
|
||||
#endif
|
||||
int pending = _udpSocket.bytesToWrite();
|
||||
int pending = _networkSocket.bytesToWrite(socketType);
|
||||
QString errorString;
|
||||
QDebug(&errorString) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError <<
|
||||
"(" << _udpSocket.errorString() << ")" << (pending ? "pending bytes:" : "pending:")
|
||||
<< pending;
|
||||
QDebug(&errorString) << "udt::Socket (" << socketTypeToString(socketType) << _networkSocket.state(socketType)
|
||||
<< ") error - " << wsaError << socketError << "(" << _networkSocket.errorString(socketType) << ")"
|
||||
<< (pending ? "pending bytes:" : "pending:") << pending;
|
||||
|
||||
if (previousWsaError.exchange(wsaError) != wsaError) {
|
||||
qCDebug(networking).noquote() << errorString;
|
||||
|
@ -564,9 +574,9 @@ void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
|
|||
}
|
||||
}
|
||||
|
||||
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
|
||||
void Socket::handleStateChanged(SocketType socketType, QAbstractSocket::SocketState socketState) {
|
||||
if (socketState != QAbstractSocket::BoundState) {
|
||||
qCDebug(networking) << "udt::Socket state changed - state is now" << socketState;
|
||||
qCDebug(networking) << socketTypeToString(socketType) << "socket state changed - state is now" << socketState;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include "../SockAddr.h"
|
||||
#include "TCPVegasCC.h"
|
||||
#include "Connection.h"
|
||||
#include "NetworkSocket.h"
|
||||
|
||||
//#define UDT_CONNECTION_DEBUG
|
||||
|
||||
|
@ -55,10 +55,10 @@ class Socket : public QObject {
|
|||
|
||||
public:
|
||||
using StatsVector = std::vector<std::pair<SockAddr, ConnectionStats::Stats>>;
|
||||
|
||||
|
||||
Socket(QObject* object = 0, bool shouldChangeSocketOptions = true);
|
||||
|
||||
quint16 localPort() const { return _udpSocket.localPort(); }
|
||||
quint16 localPort(SocketType socketType) const { return _networkSocket.localPort(socketType); }
|
||||
|
||||
// Simple functions writing to the socket with no processing
|
||||
qint64 writeBasePacket(const BasePacket& packet, const SockAddr& sockAddr);
|
||||
|
@ -68,9 +68,9 @@ public:
|
|||
qint64 writeDatagram(const char* data, qint64 size, const SockAddr& sockAddr);
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr);
|
||||
|
||||
void bind(const QHostAddress& address, quint16 port = 0);
|
||||
void rebind(quint16 port);
|
||||
void rebind();
|
||||
void bind(SocketType socketType, const QHostAddress& address, quint16 port = 0);
|
||||
void rebind(SocketType socketType, quint16 port);
|
||||
void rebind(SocketType socketType);
|
||||
|
||||
void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
|
||||
void setPacketHandler(PacketHandler handler) { _packetHandler = handler; }
|
||||
|
@ -90,6 +90,10 @@ public:
|
|||
|
||||
StatsVector sampleStatsForAllConnections();
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
const WebRTCSocket* getWebRTCSocket();
|
||||
#endif
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
void sendFakedHandshakeRequest(const SockAddr& sockAddr);
|
||||
#endif
|
||||
|
@ -106,11 +110,11 @@ private slots:
|
|||
void readPendingDatagrams();
|
||||
void checkForReadyReadBackup();
|
||||
|
||||
void handleSocketError(QAbstractSocket::SocketError socketError);
|
||||
void handleStateChanged(QAbstractSocket::SocketState socketState);
|
||||
void handleSocketError(SocketType socketType, QAbstractSocket::SocketError socketError);
|
||||
void handleStateChanged(SocketType socketType, QAbstractSocket::SocketState socketState);
|
||||
|
||||
private:
|
||||
void setSystemBufferSizes();
|
||||
void setSystemBufferSizes(SocketType socketType);
|
||||
Connection* findOrCreateConnection(const SockAddr& sockAddr, bool filterCreation = false);
|
||||
|
||||
// privatized methods used by UDTTest - they are private since they must be called on the Socket thread
|
||||
|
@ -122,7 +126,7 @@ private:
|
|||
Q_INVOKABLE void writeReliablePacket(Packet* packet, const SockAddr& sockAddr);
|
||||
Q_INVOKABLE void writeReliablePacketList(PacketList* packetList, const SockAddr& sockAddr);
|
||||
|
||||
QUdpSocket _udpSocket { this };
|
||||
NetworkSocket _networkSocket;
|
||||
PacketFilterOperator _packetFilterOperator;
|
||||
PacketHandler _packetHandler;
|
||||
MessageHandler _messageHandler;
|
||||
|
|
653
libraries/networking/src/webrtc/WebRTCDataChannels.cpp
Normal file
653
libraries/networking/src/webrtc/WebRTCDataChannels.cpp
Normal file
|
@ -0,0 +1,653 @@
|
|||
//
|
||||
// WebRTCDataChannels.cpp
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 21 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#include "WebRTCDataChannels.h"
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "../NetworkLogging.h"
|
||||
|
||||
|
||||
// References:
|
||||
// - https://webrtc.github.io/webrtc-org/native-code/native-apis/
|
||||
// - https://webrtc.googlesource.com/src/+/master/api/peer_connection_interface.h
|
||||
|
||||
// FIXME: stun:ice.vircadia.com:7337 doesn't work for WebRTC.
|
||||
// Firefox warns: "WebRTC: Using more than two STUN/TURN servers slows down discovery"
|
||||
const std::list<std::string> ICE_SERVER_URIS = {
|
||||
"stun:stun1.l.google.com:19302",
|
||||
"stun:stun.schlund.de"
|
||||
};
|
||||
const int MAX_WEBRTC_BUFFER_SIZE = 16777216; // 16MB
|
||||
|
||||
// #define WEBRTC_DEBUG
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
|
||||
void WDCSetSessionDescriptionObserver::OnSuccess() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCSetSessionDescriptionObserver::OnSuccess()";
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCSetSessionDescriptionObserver::OnFailure(RTCError error) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCSetSessionDescriptionObserver::OnFailure() :" << error.message();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
WDCCreateSessionDescriptionObserver::WDCCreateSessionDescriptionObserver(WDCConnection* parent) :
|
||||
_parent(parent)
|
||||
{ }
|
||||
|
||||
void WDCCreateSessionDescriptionObserver::OnSuccess(SessionDescriptionInterface* description) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCCreateSessionDescriptionObserver::OnSuccess()";
|
||||
#endif
|
||||
_parent->sendAnswer(description);
|
||||
_parent->setLocalDescription(description);
|
||||
}
|
||||
|
||||
void WDCCreateSessionDescriptionObserver::OnFailure(RTCError error) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCCreateSessionDescriptionObserver::OnFailure() :" << error.message();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
WDCPeerConnectionObserver::WDCPeerConnectionObserver(WDCConnection* parent) :
|
||||
_parent(parent)
|
||||
{ }
|
||||
|
||||
void WDCPeerConnectionObserver::OnSignalingChange(PeerConnectionInterface::SignalingState newState) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"Stable",
|
||||
"HaveLocalOffer",
|
||||
"HaveLocalPrAnswer",
|
||||
"HaveRemoteOffer",
|
||||
"HaveRemotePrAnswer",
|
||||
"Closed"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnSignalingChange() :" << newState << states[newState];
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnRenegotiationNeeded() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnRenegotiationNeeded()";
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnIceGatheringChange(PeerConnectionInterface::IceGatheringState newState) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"New",
|
||||
"Gathering",
|
||||
"Complete"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnIceGatheringChange() :" << newState << states[newState];
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnIceCandidate(const IceCandidateInterface* candidate) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnIceCandidate()";
|
||||
#endif
|
||||
_parent->sendIceCandidate(candidate);
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnIceConnectionChange(PeerConnectionInterface::IceConnectionState newState) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"New",
|
||||
"Checking",
|
||||
"Connected",
|
||||
"Completed",
|
||||
"Failed",
|
||||
"Disconnected",
|
||||
"Closed",
|
||||
"Max"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnIceConnectionChange() :" << newState << states[newState];
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnStandardizedIceConnectionChange(PeerConnectionInterface::IceConnectionState newState) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"New",
|
||||
"Checking",
|
||||
"Connected",
|
||||
"Completed",
|
||||
"Failed",
|
||||
"Disconnected",
|
||||
"Closed",
|
||||
"Max"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnStandardizedIceConnectionChange() :" << newState
|
||||
<< states[newState];
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnDataChannel(rtc::scoped_refptr<DataChannelInterface> dataChannel) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnDataChannel()";
|
||||
#endif
|
||||
_parent->onDataChannelOpened(dataChannel);
|
||||
}
|
||||
|
||||
void WDCPeerConnectionObserver::OnConnectionChange(PeerConnectionInterface::PeerConnectionState newState) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"New",
|
||||
"Connecting",
|
||||
"Connected",
|
||||
"Disconnected",
|
||||
"Failed",
|
||||
"Closed"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCPeerConnectionObserver::OnConnectionChange() :" << (uint)newState
|
||||
<< states[(uint)newState];
|
||||
#endif
|
||||
_parent->onPeerConnectionStateChanged(newState);
|
||||
}
|
||||
|
||||
|
||||
WDCDataChannelObserver::WDCDataChannelObserver(WDCConnection* parent) :
|
||||
_parent(parent)
|
||||
{ }
|
||||
|
||||
void WDCDataChannelObserver::OnStateChange() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCDataChannelObserver::OnStateChange()";
|
||||
#endif
|
||||
_parent->onDataChannelStateChanged();
|
||||
}
|
||||
|
||||
void WDCDataChannelObserver::OnMessage(const DataBuffer& buffer) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCDataChannelObserver::OnMessage()";
|
||||
#endif
|
||||
_parent->onDataChannelMessageReceived(buffer);
|
||||
}
|
||||
|
||||
|
||||
WDCConnection::WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID) :
|
||||
_parent(parent),
|
||||
_dataChannelID(dataChannelID)
|
||||
{
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::WDCConnection() :" << dataChannelID;
|
||||
#endif
|
||||
|
||||
// Create observers.
|
||||
_setSessionDescriptionObserver = new rtc::RefCountedObject<WDCSetSessionDescriptionObserver>();
|
||||
_createSessionDescriptionObserver = new rtc::RefCountedObject<WDCCreateSessionDescriptionObserver>(this);
|
||||
_dataChannelObserver = std::make_shared<WDCDataChannelObserver>(this);
|
||||
_peerConnectionObserver = std::make_shared<WDCPeerConnectionObserver>(this);
|
||||
|
||||
// Create new peer connection.
|
||||
_peerConnection = _parent->createPeerConnection(_peerConnectionObserver);
|
||||
};
|
||||
|
||||
void WDCConnection::setRemoteDescription(QJsonObject& description) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::setRemoteDescription() :" << description;
|
||||
#endif
|
||||
|
||||
SdpParseError sdpParseError;
|
||||
auto sessionDescription = CreateSessionDescription(
|
||||
description.value("type").toString().toStdString(),
|
||||
description.value("sdp").toString().toStdString(),
|
||||
&sdpParseError);
|
||||
if (!sessionDescription) {
|
||||
qCWarning(networking_webrtc) << "Error creating WebRTC remote description:"
|
||||
<< QString::fromStdString(sdpParseError.description);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "3. Set remote description:" << sessionDescription;
|
||||
#endif
|
||||
_peerConnection->SetRemoteDescription(_setSessionDescriptionObserver, sessionDescription);
|
||||
}
|
||||
|
||||
void WDCConnection::createAnswer() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::createAnswer()";
|
||||
qCDebug(networking_webrtc) << "4.a Create answer";
|
||||
#endif
|
||||
_peerConnection->CreateAnswer(_createSessionDescriptionObserver, PeerConnectionInterface::RTCOfferAnswerOptions());
|
||||
}
|
||||
|
||||
void WDCConnection::sendAnswer(SessionDescriptionInterface* description) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::sendAnswer()";
|
||||
qCDebug(networking_webrtc) << "4.b Send answer to the remote peer";
|
||||
#endif
|
||||
|
||||
QJsonObject jsonDescription;
|
||||
std::string descriptionString;
|
||||
description->ToString(&descriptionString);
|
||||
jsonDescription.insert("sdp", QString::fromStdString(descriptionString));
|
||||
jsonDescription.insert("type", "answer");
|
||||
|
||||
QJsonObject jsonWebRTCPayload;
|
||||
jsonWebRTCPayload.insert("description", jsonDescription);
|
||||
|
||||
QJsonObject jsonObject;
|
||||
jsonObject.insert("from", QString(_parent->getNodeType()));
|
||||
jsonObject.insert("to", _dataChannelID);
|
||||
jsonObject.insert("data", jsonWebRTCPayload);
|
||||
|
||||
_parent->sendSignalingMessage(jsonObject);
|
||||
}
|
||||
|
||||
void WDCConnection::setLocalDescription(SessionDescriptionInterface* description) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::setLocalDescription()";
|
||||
qCDebug(networking_webrtc) << "5. Set local description";
|
||||
#endif
|
||||
_peerConnection->SetLocalDescription(_setSessionDescriptionObserver, description);
|
||||
}
|
||||
|
||||
void WDCConnection::addIceCandidate(QJsonObject& data) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::addIceCandidate()";
|
||||
#endif
|
||||
|
||||
SdpParseError sdpParseError;
|
||||
auto iceCandidate = CreateIceCandidate(
|
||||
data.value("sdpMid").toString().toStdString(),
|
||||
data.value("sdpMLineIndex").toInt(),
|
||||
data.value("candidate").toString().toStdString(),
|
||||
&sdpParseError);
|
||||
if (!iceCandidate) {
|
||||
qCWarning(networking_webrtc) << "Error adding WebRTC ICE candidate:"
|
||||
<< QString::fromStdString(sdpParseError.description);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "6. Add ICE candidate";
|
||||
#endif
|
||||
_peerConnection->AddIceCandidate(iceCandidate);
|
||||
}
|
||||
|
||||
void WDCConnection::sendIceCandidate(const IceCandidateInterface* candidate) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::sendIceCandidate()";
|
||||
#endif
|
||||
|
||||
std::string candidateString;
|
||||
candidate->ToString(&candidateString);
|
||||
QJsonObject jsonCandidate;
|
||||
jsonCandidate.insert("candidate", QString::fromStdString(candidateString));
|
||||
jsonCandidate.insert("sdpMid", QString::fromStdString(candidate->sdp_mid()));
|
||||
jsonCandidate.insert("sdpMLineIndex", candidate->sdp_mline_index());
|
||||
|
||||
QJsonObject jsonWebRTCData;
|
||||
jsonWebRTCData.insert("candidate", jsonCandidate);
|
||||
|
||||
QJsonObject jsonObject;
|
||||
jsonObject.insert("from", QString(_parent->getNodeType()));
|
||||
jsonObject.insert("to", _dataChannelID);
|
||||
jsonObject.insert("data", jsonWebRTCData);
|
||||
QJsonDocument jsonDocument = QJsonDocument(jsonObject);
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "7. Send ICE candidate to the remote peer";
|
||||
#endif
|
||||
_parent->sendSignalingMessage(jsonObject);
|
||||
}
|
||||
|
||||
void WDCConnection::onPeerConnectionStateChanged(PeerConnectionInterface::PeerConnectionState state) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
QStringList states {
|
||||
"New",
|
||||
"Connecting",
|
||||
"Connected",
|
||||
"Disconnected",
|
||||
"Failed",
|
||||
"Closed"
|
||||
};
|
||||
qCDebug(networking_webrtc) << "WDCConnection::onPeerConnectionStateChanged() :" << (int)state << states[(int)state];
|
||||
#endif
|
||||
}
|
||||
|
||||
void WDCConnection::onDataChannelOpened(rtc::scoped_refptr<DataChannelInterface> dataChannel) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::onDataChannelOpened() :"
|
||||
<< dataChannel->id()
|
||||
<< QString::fromStdString(dataChannel->label())
|
||||
<< QString::fromStdString(dataChannel->protocol())
|
||||
<< dataChannel->negotiated()
|
||||
<< dataChannel->maxRetransmitTime()
|
||||
<< dataChannel->maxRetransmits()
|
||||
<< dataChannel->maxPacketLifeTime().value_or(-1)
|
||||
<< dataChannel->maxRetransmitsOpt().value_or(-1);
|
||||
#endif
|
||||
|
||||
_dataChannel = dataChannel;
|
||||
_dataChannel->RegisterObserver(_dataChannelObserver.get());
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::onDataChannelOpened() : channel ID:" << _dataChannelID;
|
||||
#endif
|
||||
_parent->onDataChannelOpened(this, _dataChannelID);
|
||||
}
|
||||
|
||||
void WDCConnection::onDataChannelStateChanged() {
|
||||
auto state = _dataChannel->state();
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::onDataChannelStateChanged() :" << (int)state
|
||||
<< DataChannelInterface::DataStateString(state);
|
||||
#endif
|
||||
if (state == DataChannelInterface::kClosed) {
|
||||
// Finish with the data channel.
|
||||
_dataChannel->UnregisterObserver();
|
||||
// Don't set _dataChannel = nullptr because it is a scoped_refptr.
|
||||
_dataChannelObserver = nullptr;
|
||||
|
||||
// Close peer connection.
|
||||
_parent->closePeerConnection(this);
|
||||
}
|
||||
}
|
||||
|
||||
void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::onDataChannelMessageReceived()";
|
||||
#endif
|
||||
|
||||
auto byteArray = QByteArray(buffer.data.data<char>(), (int)buffer.data.size());
|
||||
|
||||
// Echo message back to sender.
|
||||
if (byteArray.startsWith("echo:")) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Echo message back";
|
||||
#endif
|
||||
auto addressParts = _dataChannelID.split(":");
|
||||
if (addressParts.length() != 2) {
|
||||
qCWarning(networking_webrtc) << "Invalid dataChannelID:" << _dataChannelID;
|
||||
return;
|
||||
}
|
||||
auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt());
|
||||
_parent->sendDataMessage(address, byteArray); // Use parent method to exercise the code stack.
|
||||
return;
|
||||
}
|
||||
|
||||
_parent->emitDataMessage(_dataChannelID, byteArray);
|
||||
}
|
||||
|
||||
qint64 WDCConnection::getBufferedAmount() const {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::getBufferedAmount()";
|
||||
#endif
|
||||
return _dataChannel && _dataChannel->state() != DataChannelInterface::kClosing
|
||||
&& _dataChannel->state() != DataChannelInterface::kClosed
|
||||
? _dataChannel->buffered_amount() : 0;
|
||||
}
|
||||
|
||||
bool WDCConnection::sendDataMessage(const DataBuffer& buffer) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::sendDataMessage()";
|
||||
if (!_dataChannel || _dataChannel->state() == DataChannelInterface::kClosing
|
||||
|| _dataChannel->state() == DataChannelInterface::kClosed) {
|
||||
qCDebug(networking_webrtc) << "No data channel to send on";
|
||||
}
|
||||
#endif
|
||||
if (!_dataChannel || _dataChannel->state() == DataChannelInterface::kClosing
|
||||
|| _dataChannel->state() == DataChannelInterface::kClosed) {
|
||||
// Data channel may have been closed while message to send was being prepared.
|
||||
return false;
|
||||
} else if (_dataChannel->buffered_amount() + buffer.size() > MAX_WEBRTC_BUFFER_SIZE) {
|
||||
// Don't send, otherwise the data channel will be closed.
|
||||
qCDebug(networking_webrtc) << "WebRTC send buffer overflow";
|
||||
return false;
|
||||
}
|
||||
return _dataChannel->Send(buffer);
|
||||
}
|
||||
|
||||
void WDCConnection::closePeerConnection() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::closePeerConnection() :" << (int)_peerConnection->peer_connection_state();
|
||||
#endif
|
||||
_peerConnection->Close();
|
||||
// Don't set _peerConnection = nullptr because it is a scoped_refptr.
|
||||
_peerConnectionObserver = nullptr;
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Disposed of peer connection";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
WebRTCDataChannels::WebRTCDataChannels(QObject* parent) :
|
||||
QObject(parent),
|
||||
_parent(parent)
|
||||
{
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()";
|
||||
#endif
|
||||
|
||||
// Create a peer connection factory.
|
||||
#ifdef WEBRTC_DEBUG
|
||||
// Numbers are per WebRTC's peer_connection_interface.h.
|
||||
qCDebug(networking_webrtc) << "1. Create a new PeerConnectionFactoryInterface";
|
||||
#endif
|
||||
_rtcNetworkThread = rtc::Thread::CreateWithSocketServer();
|
||||
_rtcNetworkThread->Start();
|
||||
_rtcWorkerThread = rtc::Thread::Create();
|
||||
_rtcWorkerThread->Start();
|
||||
_rtcSignalingThread = rtc::Thread::Create();
|
||||
_rtcSignalingThread->Start();
|
||||
PeerConnectionFactoryDependencies dependencies;
|
||||
dependencies.network_thread = _rtcNetworkThread.get();
|
||||
dependencies.worker_thread = _rtcWorkerThread.get();
|
||||
dependencies.signaling_thread = _rtcSignalingThread.get();
|
||||
_peerConnectionFactory = CreateModularPeerConnectionFactory(std::move(dependencies));
|
||||
if (!_peerConnectionFactory) {
|
||||
qCWarning(networking_webrtc) << "Failed to create WebRTC peer connection factory";
|
||||
}
|
||||
|
||||
// Set up mechanism for closing peer connections.
|
||||
connect(this, &WebRTCDataChannels::closePeerConnectionSoon, this, &WebRTCDataChannels::closePeerConnectionNow);
|
||||
}
|
||||
|
||||
WebRTCDataChannels::~WebRTCDataChannels() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::~WebRTCDataChannels()";
|
||||
#endif
|
||||
reset();
|
||||
_peerConnectionFactory = nullptr;
|
||||
_rtcSignalingThread->Stop();
|
||||
_rtcSignalingThread = nullptr;
|
||||
_rtcWorkerThread->Stop();
|
||||
_rtcWorkerThread = nullptr;
|
||||
_rtcNetworkThread->Stop();
|
||||
_rtcNetworkThread = nullptr;
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::reset() {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::reset() :" << _connectionsByID.count();
|
||||
#endif
|
||||
QHashIterator<QString, WDCConnection*> i(_connectionsByID);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
delete i.value();
|
||||
}
|
||||
_connectionsByID.clear();
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::onDataChannelOpened() :" << dataChannelID;
|
||||
#endif
|
||||
_connectionsByID.insert(dataChannelID, connection);
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannel::onSignalingMessage()" << message;
|
||||
#endif
|
||||
|
||||
// Validate message.
|
||||
const int MAX_DEBUG_DETAIL_LENGTH = 64;
|
||||
const QRegularExpression DATA_CHANNEL_ID_REGEX{ "^[1-9]\\d*\\.\\d+\\.\\d+\\.\\d+:\\d+$" };
|
||||
auto data = message.value("data").isObject() ? message.value("data").toObject() : QJsonObject();
|
||||
auto from = message.value("from").toString();
|
||||
auto to = NodeType::fromChar(message.value("to").toString().at(0));
|
||||
if (!DATA_CHANNEL_ID_REGEX.match(from).hasMatch() || to == NodeType::Unassigned
|
||||
|| !data.contains("description") && !data.contains("candidate")) {
|
||||
qCWarning(networking_webrtc) << "Invalid or unexpected signaling message:"
|
||||
<< QJsonDocument(message).toJson(QJsonDocument::Compact).left(MAX_DEBUG_DETAIL_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember this node's type for the reply.
|
||||
_nodeType = to;
|
||||
|
||||
// Find or create a connection.
|
||||
WDCConnection* connection;
|
||||
if (_connectionsByID.contains(from)) {
|
||||
connection = _connectionsByID.value(from);
|
||||
} else {
|
||||
connection = new WDCConnection(this, from);
|
||||
_connectionsByID.insert(from, connection);
|
||||
}
|
||||
|
||||
// Set the remote description and reply with an answer.
|
||||
if (data.contains("description")) {
|
||||
auto description = data.value("description").toObject();
|
||||
if (description.value("type").toString() == "offer") {
|
||||
connection->setRemoteDescription(description);
|
||||
connection->createAnswer();
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Unexpected signaling description:"
|
||||
<< QJsonDocument(description).toJson(QJsonDocument::Compact).left(MAX_DEBUG_DETAIL_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a remote ICE candidate.
|
||||
if (data.contains("candidate")) {
|
||||
auto candidate = data.value("candidate").toObject();
|
||||
connection->addIceCandidate(candidate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::sendSignalingMessage(const QJsonObject& message) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::sendSignalingMessage() :" << QJsonDocument(message).toJson(QJsonDocument::Compact);
|
||||
#endif
|
||||
emit signalingMessage(message);
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray.toHex()
|
||||
<< byteArray.length();
|
||||
#endif
|
||||
auto addressParts = dataChannelID.split(":");
|
||||
if (addressParts.length() != 2) {
|
||||
qCWarning(networking_webrtc) << "Invalid dataChannelID:" << dataChannelID;
|
||||
return;
|
||||
}
|
||||
auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt());
|
||||
emit dataMessage(address, byteArray);
|
||||
}
|
||||
|
||||
bool WebRTCDataChannels::sendDataMessage(const SockAddr& destination, const QByteArray& byteArray) {
|
||||
auto dataChannelID = destination.toShortString();
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::sendDataMessage() :" << dataChannelID;
|
||||
#endif
|
||||
|
||||
if (!_connectionsByID.contains(dataChannelID)) {
|
||||
qCWarning(networking_webrtc) << "Could not find WebRTC data channel to send message on!";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto connection = _connectionsByID.value(dataChannelID);
|
||||
DataBuffer buffer(byteArray.toStdString(), true);
|
||||
return connection->sendDataMessage(buffer);
|
||||
}
|
||||
|
||||
qint64 WebRTCDataChannels::getBufferedAmount(const SockAddr& address) const {
|
||||
auto dataChannelID = address.toShortString();
|
||||
if (!_connectionsByID.contains(dataChannelID)) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::getBufferedAmount() : Channel doesn't exist:" << dataChannelID;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
auto connection = _connectionsByID.value(dataChannelID);
|
||||
return connection->getBufferedAmount();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionInterface> WebRTCDataChannels::createPeerConnection(
|
||||
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::createPeerConnection()";
|
||||
#endif
|
||||
|
||||
PeerConnectionInterface::RTCConfiguration configuration;
|
||||
for (const auto& uri : ICE_SERVER_URIS) {
|
||||
PeerConnectionInterface::IceServer iceServer;
|
||||
iceServer.uri = uri;
|
||||
configuration.servers.push_back(iceServer);
|
||||
}
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "2. Create a new peer connection";
|
||||
#endif
|
||||
PeerConnectionDependencies dependencies(peerConnectionObserver.get());
|
||||
auto result = _peerConnectionFactory->CreatePeerConnection(configuration, std::move(dependencies));
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Created peer connection";
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void WebRTCDataChannels::closePeerConnection(WDCConnection* connection) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::closePeerConnection()";
|
||||
#endif
|
||||
// Use Qt's signals/slots mechanism to close the peer connection on its own call stack, separate from the DataChannel
|
||||
// callback that initiated the peer connection.
|
||||
// https://bugs.chromium.org/p/webrtc/issues/detail?id=3721
|
||||
emit closePeerConnectionSoon(connection);
|
||||
}
|
||||
|
||||
|
||||
void WebRTCDataChannels::closePeerConnectionNow(WDCConnection* connection) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::closePeerConnectionNow()";
|
||||
#endif
|
||||
// Close the peer connection.
|
||||
connection->closePeerConnection();
|
||||
|
||||
// Delete the WDCConnection.
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Dispose of connection for channel:" << connection->getDataChannelID();
|
||||
#endif
|
||||
_connectionsByID.remove(connection->getDataChannelID());
|
||||
delete connection;
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Disposed of connection";
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
340
libraries/networking/src/webrtc/WebRTCDataChannels.h
Normal file
340
libraries/networking/src/webrtc/WebRTCDataChannels.h
Normal file
|
@ -0,0 +1,340 @@
|
|||
//
|
||||
// WebRTCDataChannels.h
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 21 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#ifndef vircadia_WebRTCDataChannels_h
|
||||
#define vircadia_WebRTCDataChannels_h
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
#undef emit // Avoid conflict between Qt signals/slots and the WebRTC library's.
|
||||
#include <api/peer_connection_interface.h>
|
||||
#define emit
|
||||
|
||||
#include "../NodeType.h"
|
||||
#include "../SockAddr.h"
|
||||
|
||||
class WebRTCDataChannels;
|
||||
class WDCConnection;
|
||||
|
||||
|
||||
/// @addtogroup Networking
|
||||
/// @{
|
||||
|
||||
/// @brief A WebRTC session description observer.
|
||||
class WDCSetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
|
||||
public:
|
||||
|
||||
/// @brief The call to SetLocalDescription or SetRemoteDescription succeeded.
|
||||
void OnSuccess() override;
|
||||
|
||||
/// @brief The call to SetLocalDescription or SetRemoteDescription failed.
|
||||
/// @param error Error information.
|
||||
void OnFailure(webrtc::RTCError error) override;
|
||||
};
|
||||
|
||||
|
||||
/// @brief A WebRTC create session description observer.
|
||||
class WDCCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver {
|
||||
public:
|
||||
|
||||
/// @brief Constructs a session description observer.
|
||||
/// @param parent The parent connection object.
|
||||
WDCCreateSessionDescriptionObserver(WDCConnection* parent);
|
||||
|
||||
/// @brief The call to CreateAnswer succeeded.
|
||||
/// @param desc The session description.
|
||||
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
|
||||
|
||||
/// @brief The call to CreateAnswer failed.
|
||||
/// @param error Error information.
|
||||
void OnFailure(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
WDCConnection* _parent;
|
||||
};
|
||||
|
||||
|
||||
/// @brief A WebRTC peer connection observer.
|
||||
class WDCPeerConnectionObserver : public webrtc::PeerConnectionObserver {
|
||||
public:
|
||||
|
||||
/// @brief Constructs a peer connection observer.
|
||||
/// @param parent The parent connection object.
|
||||
WDCPeerConnectionObserver(WDCConnection* parent);
|
||||
|
||||
/// @brief Called when the SignalingState changes.
|
||||
/// @param newState The new signaling state.
|
||||
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState newState) override;
|
||||
|
||||
/// @brief Called when renegotiation is needed. For example, an ICE restart has begun.
|
||||
void OnRenegotiationNeeded() override;
|
||||
|
||||
/// @brief Called when the ICE gather state changes.
|
||||
/// @param newState The new ICE gathering state.
|
||||
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState newState) override;
|
||||
|
||||
/// @brief Called when a new ICE candidate has been gathered.
|
||||
/// @param candidate The new ICE candidate.
|
||||
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
|
||||
|
||||
/// @brief Called when the legacy ICE connection state changes.
|
||||
/// @param new_state The new ICE connection state.
|
||||
virtual void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState newState) override;
|
||||
|
||||
/// @brief Called when the standards-compliant ICE connection state changes.
|
||||
/// @param new_state The new ICE connection state.
|
||||
virtual void OnStandardizedIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState newState) override;
|
||||
|
||||
/// @brief Called when a remote peer opens a data channel.
|
||||
/// @param dataChannel The data channel.
|
||||
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel) override;
|
||||
|
||||
/// @brief Called when the peer connection state changes.
|
||||
/// @param newState The new peer connection state.
|
||||
void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState newState) override;
|
||||
|
||||
private:
|
||||
WDCConnection* _parent;
|
||||
};
|
||||
|
||||
|
||||
/// @brief A WebRTC data channel observer.
|
||||
class WDCDataChannelObserver : public webrtc::DataChannelObserver {
|
||||
public:
|
||||
|
||||
/// @brief Constructs a data channel observer.
|
||||
/// @param parent The parent connection object.
|
||||
WDCDataChannelObserver(WDCConnection* parent);
|
||||
|
||||
/// @brief The data channel state changed.
|
||||
void OnStateChange() override;
|
||||
|
||||
/// @brief A data channel message was received.
|
||||
/// @param The message received.
|
||||
void OnMessage(const webrtc::DataBuffer& buffer) override;
|
||||
|
||||
private:
|
||||
WDCConnection* _parent;
|
||||
};
|
||||
|
||||
|
||||
/// @brief A WebRTC data channel connection.
|
||||
/// @details Opens and manages a WebRTC data channel connection.
|
||||
class WDCConnection {
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new WDCConnection and opens a WebRTC data connection.
|
||||
/// @param parent The parent WebRTCDataChannels object.
|
||||
/// @param dataChannelID The data channel ID.
|
||||
WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID);
|
||||
|
||||
/// @brief Gets the data channel ID.
|
||||
/// @return The data channel ID.
|
||||
QString getDataChannelID() const { return _dataChannelID; }
|
||||
|
||||
|
||||
/// @brief Sets the remote session description received from the remote client via the signaling channel.
|
||||
/// @param description The remote session description.
|
||||
void setRemoteDescription(QJsonObject& description);
|
||||
|
||||
/// @brief Creates an answer to an offer received from the remote client via the signaling channel.
|
||||
void createAnswer();
|
||||
|
||||
/// @brief Sends an answer to the remote client via the signaling channel.
|
||||
/// @param description The answer.
|
||||
void sendAnswer(webrtc::SessionDescriptionInterface* description);
|
||||
|
||||
/// @brief Sets the local session description on the WebRTC data channel being connected.
|
||||
/// @param description The local session description.
|
||||
void setLocalDescription(webrtc::SessionDescriptionInterface* description);
|
||||
|
||||
/// @brief Adds an ICE candidate received from the remote client via the signaling channel.
|
||||
/// @param data The ICE candidate.
|
||||
void addIceCandidate(QJsonObject& data);
|
||||
|
||||
/// @brief Sends an ICE candidate to the remote client via the signaling channel.
|
||||
/// @param candidate The ICE candidate.
|
||||
void sendIceCandidate(const webrtc::IceCandidateInterface* candidate);
|
||||
|
||||
/// @brief Monitors the peer connection state.
|
||||
/// @param state The new peer connection state.
|
||||
void onPeerConnectionStateChanged(webrtc::PeerConnectionInterface::PeerConnectionState state);
|
||||
|
||||
/// @brief Handles the WebRTC data channel being opened.
|
||||
/// @param dataChannel The WebRTC data channel.
|
||||
void onDataChannelOpened(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel);
|
||||
|
||||
/// @brief Handles a change in the state of the WebRTC data channel.
|
||||
void onDataChannelStateChanged();
|
||||
|
||||
|
||||
/// @brief Handles a message being received on the WebRTC data channel.
|
||||
/// @param buffer The message received.
|
||||
void onDataChannelMessageReceived(const webrtc::DataBuffer& buffer);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be sent on the WebRTC data channel.
|
||||
/// @return The number of bytes waiting to be sent on the WebRTC data channel.
|
||||
qint64 getBufferedAmount() const;
|
||||
|
||||
|
||||
/// @brief Sends a message on the WebRTC data channel.
|
||||
/// @param buffer The message to send.
|
||||
/// @return `true` if the message was sent, otherwise `false`.
|
||||
bool sendDataMessage(const webrtc::DataBuffer& buffer);
|
||||
|
||||
/// @brief Closes the WebRTC peer connection.
|
||||
void closePeerConnection();
|
||||
|
||||
private:
|
||||
WebRTCDataChannels* _parent;
|
||||
QString _dataChannelID;
|
||||
|
||||
rtc::scoped_refptr<WDCSetSessionDescriptionObserver> _setSessionDescriptionObserver { nullptr };
|
||||
rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { nullptr };
|
||||
|
||||
std::shared_ptr<WDCDataChannelObserver> _dataChannelObserver { nullptr };
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> _dataChannel { nullptr };
|
||||
|
||||
std::shared_ptr<WDCPeerConnectionObserver> _peerConnectionObserver { nullptr };
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection { nullptr };
|
||||
};
|
||||
|
||||
|
||||
/// @brief Manages WebRTC data channels on the domain server or an assignment client that Interface clients can connect to.
|
||||
///
|
||||
/// @details Presents multiple individual WebRTC data channels as a single one-to-many WebRTCDataChannels object. Interface
|
||||
/// clients may use WebRTC data channels for Vircadia protocol network communications instead of UDP.
|
||||
/// A WebRTCSignalingServer is used in the process of setting up a WebRTC data channel between an Interface client and the
|
||||
/// domain server or assignment client.
|
||||
/// The Interface client initiates the connection - including initiating the data channel - and the domain server or assignment
|
||||
/// client responds.
|
||||
///
|
||||
/// Additionally, for debugging purposes, instead of containing a Vircadia protocol payload, a WebRTC message may be an echo
|
||||
/// request. This is bounced back to the client.
|
||||
///
|
||||
/// A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening the data
|
||||
/// channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC
|
||||
/// connection is not used.
|
||||
class WebRTCDataChannels : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new WebRTCDataChannels object.
|
||||
/// @param parent The parent Qt object.
|
||||
WebRTCDataChannels(QObject* parent);
|
||||
|
||||
/// @brief Destroys a WebRTCDataChannels object.
|
||||
~WebRTCDataChannels();
|
||||
|
||||
/// @brief Gets the type of node that the WebRTCDataChannels object is being used in.
|
||||
/// @return The type of node.
|
||||
NodeType_t getNodeType() {
|
||||
return _nodeType;
|
||||
}
|
||||
|
||||
/// @brief Immediately closes all connections and resets the socket.
|
||||
void reset();
|
||||
|
||||
/// @brief Handles a WebRTC data channel opening.
|
||||
/// @param connection The WebRTC data channel connection.
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
void onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID);
|
||||
|
||||
/// @brief Emits a signalingMessage to be sent to the Interface client.
|
||||
/// @param message The WebRTC signaling message to send.
|
||||
void sendSignalingMessage(const QJsonObject& message);
|
||||
|
||||
/// @brief Emits a dataMessage received from the Interface client.
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
/// @param byteArray The data message received.
|
||||
void emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray);
|
||||
|
||||
/// @brief Sends a data message to an Interface client.
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
/// @param message The data message to send.
|
||||
/// @return `true` if the data message was sent, otherwise `false`.
|
||||
bool sendDataMessage(const SockAddr& destination, const QByteArray& message);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be sent on a data channel.
|
||||
/// @param address The address of the signaling WebSocket that the client used to connect.
|
||||
/// @return The number of bytes waiting to be sent on the data channel.
|
||||
qint64 getBufferedAmount(const SockAddr& address) const;
|
||||
|
||||
/// @brief Creates a new WebRTC peer connection for connecting to an Interface client.
|
||||
/// @param peerConnectionObserver An observer to monitor the WebRTC peer connection.
|
||||
/// @return The new WebRTC peer connection.
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> createPeerConnection(
|
||||
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver);
|
||||
|
||||
/// @brief Initiates closing the peer connection for a WebRTC data channel.
|
||||
/// @details Emits a {@link WebRTCDataChannels.closePeerConnectionSoon} signal which is connected to
|
||||
/// {@link WebRTCDataChannels.closePeerConnectionNow} in order to close the peer connection on a new call stack. This is
|
||||
/// necessary to work around a WebRTC library limitation.
|
||||
/// @param connection The WebRTC data channel connection.
|
||||
void closePeerConnection(WDCConnection* connection);
|
||||
|
||||
public slots:
|
||||
|
||||
/// @brief Handles a WebRTC signaling message received from the Interface client.
|
||||
/// @param message The WebRTC signaling message.
|
||||
void onSignalingMessage(const QJsonObject& message);
|
||||
|
||||
/// @brief Closes the peer connection for a WebRTC data channel.
|
||||
/// @details Used by {@link WebRTCDataChannels.closePeerConnection}.
|
||||
/// @param connection The WebRTC data channel connection.
|
||||
void closePeerConnectionNow(WDCConnection* connection);
|
||||
|
||||
signals:
|
||||
|
||||
/// @brief A WebRTC signaling message to be sent to the Interface client.
|
||||
/// @details This message is for the WebRTCSignalingServer to send.
|
||||
/// @param message The WebRTC signaling message to send.
|
||||
void signalingMessage(const QJsonObject& message);
|
||||
|
||||
/// @brief A WebRTC data message received from the Interface client.
|
||||
/// @details This message is for handling at a higher level in the Vircadia protocol.
|
||||
/// @param address The address of the signaling WebSocket that the client used to connect.
|
||||
/// @param byteArray The Vircadia protocol message.
|
||||
void dataMessage(const SockAddr& address, const QByteArray& byteArray);
|
||||
|
||||
/// @brief Signals that the peer connection for a WebRTC data channel should be closed.
|
||||
/// @details Used by {@link WebRTCDataChannels.closePeerConnection}.
|
||||
/// @param connection The WebRTC data channel connection.
|
||||
void closePeerConnectionSoon(WDCConnection* connection);
|
||||
|
||||
private:
|
||||
|
||||
QObject* _parent;
|
||||
|
||||
NodeType_t _nodeType { NodeType::Unassigned };
|
||||
|
||||
std::unique_ptr<rtc::Thread> _rtcNetworkThread { nullptr };
|
||||
std::unique_ptr<rtc::Thread> _rtcWorkerThread { nullptr };
|
||||
std::unique_ptr<rtc::Thread> _rtcSignalingThread { nullptr };
|
||||
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr };
|
||||
|
||||
QHash<QString, WDCConnection*> _connectionsByID; // <client data channel ID, WDCConnection>
|
||||
// The client's WebSocket IP and port is used as the data channel ID to uniquely identify each.
|
||||
// The WebSocket IP address and port is formatted as "n.n.n.n:n", the same as used in WebRTCSignalingServer.
|
||||
};
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
||||
|
||||
#endif // vircadia_WebRTCDataChannels_h
|
147
libraries/networking/src/webrtc/WebRTCSignalingServer.cpp
Normal file
147
libraries/networking/src/webrtc/WebRTCSignalingServer.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
//
|
||||
// WebRTCSignalingServer.cpp
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 16 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#include "WebRTCSignalingServer.h"
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
#include <QSslKey>
|
||||
#include <QtCore>
|
||||
#include <QWebSocket>
|
||||
|
||||
#include <BuildInfo.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "../NetworkLogging.h"
|
||||
#include "../NodeType.h"
|
||||
|
||||
|
||||
const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000;
|
||||
|
||||
WebRTCSignalingServer::WebRTCSignalingServer(QObject* parent, bool isWSSEnabled) :
|
||||
QObject(parent)
|
||||
{
|
||||
if (isWSSEnabled) {
|
||||
_webSocketServer = (new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::SecureMode,
|
||||
this));
|
||||
|
||||
auto dsDirPath = PathUtils::getAppLocalDataPath();
|
||||
const QString KEY_FILENAME = "vircadia-cert.key";
|
||||
const QString CRT_FILENAME = "vircadia-cert.crt";
|
||||
const QString CA_CRT_FILENAME = "vircadia-cert-ca.crt";
|
||||
qCDebug(networking_webrtc) << "WebSocket WSS key file:" << dsDirPath + KEY_FILENAME;
|
||||
qCDebug(networking_webrtc) << "WebSocket WSS cert file:" << dsDirPath + CRT_FILENAME;
|
||||
qCDebug(networking_webrtc) << "WebSocket WSS CA cert file:" << dsDirPath + CA_CRT_FILENAME;
|
||||
|
||||
QFile sslCaFile(dsDirPath + CA_CRT_FILENAME);
|
||||
sslCaFile.open(QIODevice::ReadOnly);
|
||||
QSslCertificate sslCaCertificate(&sslCaFile, QSsl::Pem);
|
||||
sslCaFile.close();
|
||||
|
||||
QSslConfiguration sslConfiguration;
|
||||
QFile sslCrtFile(dsDirPath + CRT_FILENAME);
|
||||
sslCrtFile.open(QIODevice::ReadOnly);
|
||||
QSslCertificate sslCertificate(&sslCrtFile, QSsl::Pem);
|
||||
sslCrtFile.close();
|
||||
|
||||
QFile sslKeyFile(dsDirPath + KEY_FILENAME);
|
||||
sslKeyFile.open(QIODevice::ReadOnly);
|
||||
QSslKey sslKey(&sslKeyFile, QSsl::Rsa, QSsl::Pem);
|
||||
sslKeyFile.close();
|
||||
|
||||
if (!sslCaCertificate.isNull() && !sslKey.isNull() && !sslCertificate.isNull()) {
|
||||
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
sslConfiguration.addCaCertificate(sslCaCertificate);
|
||||
sslConfiguration.setLocalCertificate(sslCertificate);
|
||||
sslConfiguration.setPrivateKey(sslKey);
|
||||
_webSocketServer->setSslConfiguration(sslConfiguration);
|
||||
qCDebug(networking_webrtc) << "WebSocket SSL mode enabled:"
|
||||
<< (_webSocketServer->secureMode() == QWebSocketServer::SecureMode);
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Error creating WebSocket SSL key.";
|
||||
}
|
||||
|
||||
} else {
|
||||
_webSocketServer = (new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::NonSecureMode,
|
||||
this));
|
||||
}
|
||||
connect(_webSocketServer, &QWebSocketServer::newConnection, this, &WebRTCSignalingServer::newWebSocketConnection);
|
||||
|
||||
// Automatically recover from network interruptions.
|
||||
_isWebSocketServerListeningTimer = new QTimer(this);
|
||||
connect(_isWebSocketServerListeningTimer, &QTimer::timeout, this, &WebRTCSignalingServer::checkWebSocketServerIsListening);
|
||||
_isWebSocketServerListeningTimer->start(WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS);
|
||||
}
|
||||
|
||||
bool WebRTCSignalingServer::bind(const QHostAddress& address, quint16 port) {
|
||||
_address = address;
|
||||
_port = port;
|
||||
auto success = _webSocketServer->listen(_address, _port);
|
||||
if (!success) {
|
||||
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::checkWebSocketServerIsListening() {
|
||||
if (!_webSocketServer->isListening()) {
|
||||
qCWarning(networking_webrtc) << "WebSocket on port " << QString::number(_port) << " is no longer listening";
|
||||
_webSockets.clear();
|
||||
_webSocketServer->listen(_address, _port);
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& message) {
|
||||
auto source = qobject_cast<QWebSocket*>(sender());
|
||||
if (source) {
|
||||
QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
|
||||
// WEBRTC TODO: Move domain server echoing into domain server.
|
||||
if (json.keys().contains("echo") && json.value("to").toString() == QString(QChar(NodeType::DomainServer))) {
|
||||
// Domain server echo request - echo message back to sender.
|
||||
json.remove("to");
|
||||
json.insert("from", QString(QChar(NodeType::DomainServer)));
|
||||
QString echo = QJsonDocument(json).toJson();
|
||||
source->sendTextMessage(echo);
|
||||
} else {
|
||||
// WebRTC message or assignment client echo request. (Send both to target.)
|
||||
auto from = source->peerAddress().toString() + ":" + QString::number(source->peerPort());
|
||||
json.insert("from", from);
|
||||
emit messageReceived(json);
|
||||
}
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Failed to find WebSocket for incoming WebRTC signaling message.";
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::sendMessage(const QJsonObject& message) {
|
||||
auto destinationAddress = message.value("to").toString();
|
||||
if (_webSockets.contains(destinationAddress)) {
|
||||
_webSockets.value(destinationAddress)->sendTextMessage(QString(QJsonDocument(message).toJson()));
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Failed to find WebSocket for outgoing WebRTC signaling message.";
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::webSocketDisconnected() {
|
||||
auto source = qobject_cast<QWebSocket*>(sender());
|
||||
if (source) {
|
||||
auto address = source->peerAddress().toString() + ":" + QString::number(source->peerPort());
|
||||
_webSockets.remove(address);
|
||||
source->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::newWebSocketConnection() {
|
||||
auto webSocket = _webSocketServer->nextPendingConnection();
|
||||
connect(webSocket, &QWebSocket::textMessageReceived, this, &WebRTCSignalingServer::webSocketTextMessageReceived);
|
||||
connect(webSocket, &QWebSocket::disconnected, this, &WebRTCSignalingServer::webSocketDisconnected);
|
||||
auto webSocketAddress = webSocket->peerAddress().toString() + ":" + QString::number(webSocket->peerPort());
|
||||
_webSockets.insert(webSocketAddress, webSocket);
|
||||
}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
113
libraries/networking/src/webrtc/WebRTCSignalingServer.h
Normal file
113
libraries/networking/src/webrtc/WebRTCSignalingServer.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// WebRTCSignalingServer.h
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 16 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#ifndef vircadia_WebRTCSignalingServer_h
|
||||
#define vircadia_WebRTCSignalingServer_h
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
#include <QObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QWebSocketServer>
|
||||
|
||||
#include "../SockAddr.h"
|
||||
|
||||
/// @addtogroup Networking
|
||||
/// @{
|
||||
|
||||
/// @brief Provides a WebRTC signaling server that Interface clients can use to initiate WebRTC connections to the domain server
|
||||
/// and its assignment clients.
|
||||
///
|
||||
/// @details The signaling server is expected to be hosted in the domain server. It provides a WebSocket for Interface clients
|
||||
/// to use in the WebRTC signaling handshake process to establish WebRTC data channel connections to each of the domain server
|
||||
/// and the assignment clients (i.e., separate WebRTC data channels for each but only a single signaling WebSocket). The
|
||||
/// assignment client signaling messages are expected to be relayed - by the domain server - via Vircadia protocol messages on
|
||||
/// the UDP connections between the domain server and assignment clients.
|
||||
///
|
||||
/// Additionally, for debugging purposes, instead of containing a WebRTC payload a signaling message may be an echo request.
|
||||
/// This is bounced back to the client from the WebRTCSignalingServer if the domain server was the target, otherwise it is
|
||||
/// expected to be bounced back upon receipt by the relevant assignment client.
|
||||
///
|
||||
/// The signaling messages are sent and received as JSON objects, with `to` and `from` fields in addition to either the WebRTC
|
||||
/// signaling `data` payload or an `echo` request:
|
||||
///
|
||||
/// | Interface -> Server ||
|
||||
/// | -------- | ---------------------------------------- |
|
||||
/// | `to` | NodeType |
|
||||
/// | `from` | WebSocket IP address & port, "n.n.n.n:n" |
|
||||
/// | [`data`] | WebRTC signaling payload |
|
||||
/// | [`echo`] | Echo request |
|
||||
///
|
||||
/// `*` The `from` field is filled in upon receipt by the WebRTCSignalingServer.
|
||||
///
|
||||
/// | Server -> Interface ||
|
||||
/// | -------- | ---------------------------------------- |
|
||||
/// | `to` | WebSocket IP address & port, "n.n.n.n:n" |
|
||||
/// | `from` | NodeType |
|
||||
/// | [`data`] | WebRTC signaling payload |
|
||||
/// | [`echo`] | Echo response |
|
||||
///
|
||||
class WebRTCSignalingServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new WebRTCSignalingServer object.
|
||||
/// @param parent Qt parent object.
|
||||
/// @param isWSSEnabled Whether the WebSocket used for WebRTC signaling should be secure (WSS protocol).
|
||||
WebRTCSignalingServer(QObject* parent, bool isWSSEnabled);
|
||||
|
||||
/// @brief Binds the WebRTC signaling server's WebSocket to an address and port.
|
||||
/// @param address The address to use for the WebSocket.
|
||||
/// @param port The port to use for the WebSocket.
|
||||
/// @return <code>true</code> if the WebSocket was successfully bound, <code>false</code> if it wasn't.
|
||||
bool bind(const QHostAddress& address, quint16 port);
|
||||
|
||||
public slots:
|
||||
|
||||
/// @brief Send a WebRTC signaling channel message to an Interface client.
|
||||
/// @param message The message to send to the Interface client. Includes details of the sender and the destination in
|
||||
/// addition to the WebRTC signaling channel payload.
|
||||
void sendMessage(const QJsonObject& message);
|
||||
|
||||
signals:
|
||||
|
||||
/// @brief A WebRTC signaling channel message was received from an Interface client.
|
||||
/// @param message The message received from the Interface client. Includes details of the sender and the destination in
|
||||
/// addition to the WebRTC signaling channel payload.\n
|
||||
/// Not emitted if the message was an echo request for the domain server.
|
||||
void messageReceived(const QJsonObject& message);
|
||||
|
||||
private slots:
|
||||
|
||||
void newWebSocketConnection();
|
||||
void webSocketTextMessageReceived(const QString& message);
|
||||
void webSocketDisconnected();
|
||||
|
||||
private:
|
||||
|
||||
void checkWebSocketServerIsListening();
|
||||
|
||||
QWebSocketServer* _webSocketServer;
|
||||
QHostAddress _address;
|
||||
quint16 _port { 0 };
|
||||
|
||||
QHash<QString, QWebSocket*> _webSockets; // <client WebSocket IP address and port, client connection WebSocket object>
|
||||
// The WebSocket IP address and port is formatted as "n.n.n.n:n".
|
||||
// A QString is used rather than a SockAddr, to make signaling easier.
|
||||
|
||||
QTimer* _isWebSocketServerListeningTimer;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
||||
|
||||
#endif // vircadia_WebRTCSignalingServer_h
|
155
libraries/networking/src/webrtc/WebRTCSocket.cpp
Normal file
155
libraries/networking/src/webrtc/WebRTCSocket.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// WebRTCSocket.cpp
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 21 Jun 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#include "WebRTCSocket.h"
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "../NetworkLogging.h"
|
||||
#include "../udt/Constants.h"
|
||||
|
||||
|
||||
WebRTCSocket::WebRTCSocket(QObject* parent) :
|
||||
QObject(parent),
|
||||
_dataChannels(this)
|
||||
{
|
||||
// Route signaling messages.
|
||||
connect(this, &WebRTCSocket::onSignalingMessage, &_dataChannels, &WebRTCDataChannels::onSignalingMessage);
|
||||
connect(&_dataChannels, &WebRTCDataChannels::signalingMessage, this, &WebRTCSocket::sendSignalingMessage);
|
||||
|
||||
// Route received data channel messages.
|
||||
connect(&_dataChannels, &WebRTCDataChannels::dataMessage, this, &WebRTCSocket::onDataChannelReceivedMessage);
|
||||
}
|
||||
|
||||
void WebRTCSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant& value) {
|
||||
clearError();
|
||||
switch (option) {
|
||||
case QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption:
|
||||
case QAbstractSocket::SocketOption::SendBufferSizeSocketOption:
|
||||
// WebRTC doesn't provide access to setting these buffer sizes.
|
||||
break;
|
||||
default:
|
||||
setError(QAbstractSocket::SocketError::UnsupportedSocketOperationError, "Failed to set socket option");
|
||||
qCCritical(networking_webrtc) << "WebRTCSocket::setSocketOption() not implemented for option:" << option;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QVariant WebRTCSocket::socketOption(QAbstractSocket::SocketOption option) {
|
||||
clearError();
|
||||
switch (option) {
|
||||
case QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption:
|
||||
// WebRTC doesn't provide access to the receive buffer size. Just use the default buffer size.
|
||||
return udt::WEBRTC_RECEIVE_BUFFER_SIZE_BYTES;
|
||||
case QAbstractSocket::SocketOption::SendBufferSizeSocketOption:
|
||||
// WebRTC doesn't provide access to the send buffer size though it's probably 16MB. Just use the default buffer size.
|
||||
return udt::WEBRTC_SEND_BUFFER_SIZE_BYTES;
|
||||
default:
|
||||
setError(QAbstractSocket::SocketError::UnsupportedSocketOperationError, "Failed to get socket option");
|
||||
qCCritical(networking_webrtc) << "WebRTCSocket::getSocketOption() not implemented for option:" << option;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool WebRTCSocket::bind(const QHostAddress& address, quint16 port, QAbstractSocket::BindMode mode) {
|
||||
// WebRTC data channels aren't bound to ports so just treat this as a successful operation.
|
||||
auto wasBound = _isBound;
|
||||
_isBound = true;
|
||||
if (_isBound != wasBound) {
|
||||
emit stateChanged(_isBound ? QAbstractSocket::BoundState : QAbstractSocket::UnconnectedState);
|
||||
}
|
||||
return _isBound;
|
||||
}
|
||||
|
||||
QAbstractSocket::SocketState WebRTCSocket::state() const {
|
||||
return _isBound ? QAbstractSocket::BoundState : QAbstractSocket::UnconnectedState;
|
||||
}
|
||||
|
||||
void WebRTCSocket::abort() {
|
||||
_dataChannels.reset();
|
||||
}
|
||||
|
||||
|
||||
qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, const SockAddr& destination) {
|
||||
clearError();
|
||||
if (_dataChannels.sendDataMessage(destination, datagram)) {
|
||||
return datagram.length();
|
||||
}
|
||||
setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to write datagram");
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 WebRTCSocket::bytesToWrite(const SockAddr& destination) const {
|
||||
return _dataChannels.getBufferedAmount(destination);
|
||||
}
|
||||
|
||||
|
||||
bool WebRTCSocket::hasPendingDatagrams() const {
|
||||
return _receivedQueue.length() > 0;
|
||||
}
|
||||
|
||||
qint64 WebRTCSocket::pendingDatagramSize() const {
|
||||
if (_receivedQueue.length() > 0) {
|
||||
return _receivedQueue.head().second.length();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 WebRTCSocket::readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) {
|
||||
clearError();
|
||||
if (_receivedQueue.length() > 0) {
|
||||
auto datagram = _receivedQueue.dequeue();
|
||||
auto length = std::min((qint64)datagram.second.length(), maxSize);
|
||||
|
||||
if (data) {
|
||||
memcpy(data, datagram.second.constData(), length);
|
||||
}
|
||||
|
||||
if (address) {
|
||||
*address = datagram.first.getAddress();
|
||||
}
|
||||
|
||||
if (port) {
|
||||
*port = datagram.first.getPort();
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to read datagram");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
QAbstractSocket::SocketError WebRTCSocket::error() const {
|
||||
return _lastErrorType;
|
||||
}
|
||||
|
||||
QString WebRTCSocket::errorString() const {
|
||||
return _lastErrorString;
|
||||
}
|
||||
|
||||
|
||||
void WebRTCSocket::setError(QAbstractSocket::SocketError errorType, QString errorString) {
|
||||
_lastErrorType = errorType;
|
||||
}
|
||||
|
||||
void WebRTCSocket::clearError() {
|
||||
_lastErrorType = QAbstractSocket::SocketError();
|
||||
_lastErrorString = QString();
|
||||
}
|
||||
|
||||
|
||||
void WebRTCSocket::onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message) {
|
||||
_receivedQueue.enqueue(QPair<SockAddr, QByteArray>(source, message));
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
171
libraries/networking/src/webrtc/WebRTCSocket.h
Normal file
171
libraries/networking/src/webrtc/WebRTCSocket.h
Normal file
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// WebRTCSocket.h
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 21 Jun 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#ifndef vircadia_WebRTCSocket_h
|
||||
#define vircadia_WebRTCSocket_h
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
|
||||
#include <QAbstractSocket>
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
|
||||
#include "WebRTCDataChannels.h"
|
||||
|
||||
/// @addtogroup Networking
|
||||
/// @{
|
||||
|
||||
|
||||
/// @brief Provides a QUdpSocket-style interface for using WebRTCDataChannels.
|
||||
///
|
||||
/// @details A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening
|
||||
/// the data channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC
|
||||
/// connection is not used.
|
||||
class WebRTCSocket : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new WebRTCSocket object.
|
||||
/// @param parent Qt parent object.
|
||||
WebRTCSocket(QObject* parent);
|
||||
|
||||
|
||||
/// @brief Nominally sets the value of a socket option.
|
||||
/// @details Only <code>SendBufferSizeSocketOption</code> and <code>ReceiveBufferSizeSocketOption</code> options are handled
|
||||
/// and for these no action is taken because these buffer sizes are not configurable in WebRTC.
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @param option The socket option.
|
||||
/// @param value The value of the socket option.
|
||||
void setSocketOption(QAbstractSocket::SocketOption option, const QVariant& value);
|
||||
|
||||
/// @brief Nominally gets the value of a socket option.
|
||||
/// @details Only <code>SendBufferSizeSocketOption</code> and <code>ReceiveBufferSizeSocketOption</code> options are handled
|
||||
/// and for these only default values are returned because these buffer sizes are not configurable in WebRTC.
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @param option The socket option.
|
||||
/// @return The value of the socket option.
|
||||
QVariant socketOption(QAbstractSocket::SocketOption option);
|
||||
|
||||
/// @brief Nominally binds the WebRTC socket to an address and port.
|
||||
/// @details WebRTC data connections aren't actually bound to an address or port. Their ports are negotiated as part of the
|
||||
/// WebRTC peer connection process.
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @param address The address.
|
||||
/// @param port The port.
|
||||
/// @param mode The bind mode.
|
||||
/// @return <code>true</code>.
|
||||
bool bind(const QHostAddress& address, quint16 port = 0, QAbstractSocket::BindMode mode
|
||||
= QAbstractSocket::DefaultForPlatform);
|
||||
|
||||
/// @brief Gets the state of the socket.
|
||||
/// @details In particular, QAbstractSocket::BoundState is returned if the socket is bound,
|
||||
/// QAbstractSocket::UnconnectedState if it isn't.
|
||||
/// @return The state of the socket.
|
||||
QAbstractSocket::SocketState state() const;
|
||||
|
||||
/// @brief Immediately closes all connections and resets the socket.
|
||||
void abort();
|
||||
|
||||
/// @brief Nominally gets the host port number.
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @return <code>0</code>
|
||||
quint16 localPort() const { return 0; }
|
||||
|
||||
/// @brief Nominally gets the socket descriptor.
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @return <code>-1</code>
|
||||
qintptr socketDescriptor() const { return -1; }
|
||||
|
||||
|
||||
/// @brief Sends a datagram.
|
||||
/// @param datagram The datagram to send.
|
||||
/// @param destination The destination WebRTC data channel address.
|
||||
/// @return The number of bytes if successfully sent, otherwise <code>-1</code>.
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SockAddr& destination);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be written.
|
||||
/// @param destination The destination WebRTC data channel address.
|
||||
/// @return The number of bytes waiting to be written.
|
||||
qint64 bytesToWrite(const SockAddr& destination) const;
|
||||
|
||||
/// @brief Gets whether there's a datagram waiting to be read.
|
||||
/// @return <code>true</code> if there's a datagram waiting to be read, <code>false</code> if there isn't.
|
||||
bool hasPendingDatagrams() const;
|
||||
|
||||
/// @brief Gets the size of the first pending datagram.
|
||||
/// @return the size of the first pending datagram; <code>-1</code> if there is no pending datagram.
|
||||
qint64 pendingDatagramSize() const;
|
||||
|
||||
/// @brief Reads the next datagram, up to a maximum number of bytes.
|
||||
/// @details Any remaining data in the datagram is lost.
|
||||
/// @param data The destination to read the datagram into.
|
||||
/// @param maxSize The maximum number of bytes to read.
|
||||
/// @param address The destination to put the WebRTC data channel's IP address.
|
||||
/// @param port The destination to put the WebRTC data channel's port.
|
||||
/// @return The number of bytes read on success; <code>-1</code> if reading unsuccessful.
|
||||
qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address = nullptr, quint16* port = nullptr);
|
||||
|
||||
|
||||
/// @brief Gets the type of error that last occurred.
|
||||
/// @return The type of error that last occurred.
|
||||
QAbstractSocket::SocketError error() const;
|
||||
|
||||
/// @brief Gets the description of the error that last occurred.
|
||||
/// @return The description of the error that last occurred.
|
||||
QString errorString() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/// @brief Handles the WebRTC data channel receiving a message.
|
||||
/// @details Queues the message to be read via readDatagram.
|
||||
/// @param source The WebRTC data channel that the message was received on.
|
||||
/// @param message The message that was received.
|
||||
void onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message);
|
||||
|
||||
signals:
|
||||
|
||||
/// @brief Emitted when the state of the socket changes.
|
||||
/// @param socketState The new state of the socket.
|
||||
void stateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
/// @brief Emitted each time new data becomes available for reading.
|
||||
void readyRead();
|
||||
|
||||
/// @brief Emitted when a WebRTC signaling message has been received from the signaling server for this WebRTCSocket.
|
||||
/// @param json The signaling message.
|
||||
void onSignalingMessage(const QJsonObject& json);
|
||||
|
||||
/// @brief Emitted when there's a WebRTC signaling message to send via the signaling server.
|
||||
/// @param json The signaling message.
|
||||
void sendSignalingMessage(const QJsonObject& message);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void setError(QAbstractSocket::SocketError errorType, QString errorString);
|
||||
void clearError();
|
||||
|
||||
WebRTCDataChannels _dataChannels;
|
||||
|
||||
bool _isBound { false };
|
||||
|
||||
QQueue<QPair<SockAddr, QByteArray>> _receivedQueue; // Messages received are queued for reading from the "socket".
|
||||
|
||||
QAbstractSocket::SocketError _lastErrorType { QAbstractSocket::UnknownSocketError };
|
||||
QString _lastErrorString;
|
||||
};
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
||||
|
||||
#endif // vircadia_WebRTCSocket_h
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
using namespace platform;
|
||||
|
||||
const std::array<const char*, Profiler::Tier::NumTiers> Profiler::TierNames = {{ "UNKNOWN", "LOW", "MID", "HIGH" }};
|
||||
const std::array<const char*, Profiler::Tier::NumTiers> Profiler::TierNames = {{ "UNKNOWN", "LOW_POWER", "LOW", "MID", "HIGH" }};
|
||||
|
||||
|
||||
bool filterOnComputer(const platform::json& computer, Profiler::Tier& tier);
|
||||
|
|
|
@ -19,6 +19,7 @@ class Profiler {
|
|||
public:
|
||||
enum Tier {
|
||||
UNKNOWN = 0,
|
||||
LOW_POWER,
|
||||
LOW,
|
||||
MID,
|
||||
HIGH,
|
||||
|
|
|
@ -140,7 +140,7 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
|
|||
#else
|
||||
|
||||
void AntialiasingConfig::setAAMode(int mode) {
|
||||
_mode = std::min((int)AntialiasingConfig::MODE_COUNT, std::max(0, mode));
|
||||
_mode = std::min((int)AntialiasingConfig::MODE_COUNT, std::max(0, mode)); // Just use unsigned?
|
||||
emit dirty();
|
||||
}
|
||||
|
||||
|
|
|
@ -110,11 +110,12 @@ public:
|
|||
AntialiasingConfig() : render::Job::Config(true) {}
|
||||
|
||||
enum Mode {
|
||||
OFF = 0,
|
||||
NONE = 0,
|
||||
TAA,
|
||||
FXAA,
|
||||
MODE_COUNT
|
||||
};
|
||||
Q_ENUM(Mode) // Stored as signed int.
|
||||
|
||||
void setAAMode(int mode);
|
||||
int getAAMode() const { return _mode; }
|
||||
|
@ -122,7 +123,7 @@ public:
|
|||
void setDebugFXAA(bool debug) { debugFXAAX = (debug ? 0.0f : 1.0f); emit dirty();}
|
||||
bool debugFXAA() const { return (debugFXAAX == 0.0f ? true : false); }
|
||||
|
||||
int _mode{ TAA };
|
||||
int _mode{ TAA }; // '_' prefix but not private?
|
||||
|
||||
float blend{ 0.25f };
|
||||
float sharpen{ 0.05f };
|
||||
|
@ -216,8 +217,8 @@ private:
|
|||
};
|
||||
|
||||
|
||||
#else
|
||||
class AntiAliasingConfig : public render::Job::Config {
|
||||
#else // User setting for antialias mode will probably be broken.
|
||||
class AntiAliasingConfig : public render::Job::Config { // Not to be confused with AntialiasingConfig...
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled MEMBER enabled)
|
||||
public:
|
||||
|
@ -236,7 +237,7 @@ public:
|
|||
|
||||
const gpu::PipelinePointer& getAntialiasingPipeline();
|
||||
const gpu::PipelinePointer& getBlendPipeline();
|
||||
|
||||
|
||||
private:
|
||||
gpu::FramebufferPointer _antialiasingBuffer;
|
||||
|
||||
|
|
|
@ -295,7 +295,20 @@ bool Model::updateGeometry() {
|
|||
// TODO: should all Models have a valid _rig?
|
||||
if (_rig.jointStatesEmpty() && getHFMModel().joints.size() > 0) {
|
||||
initJointStates();
|
||||
assert(_meshStates.empty());
|
||||
|
||||
if (!_meshStates.empty()) {
|
||||
// See https://github.com/vircadia/vircadia/issues/958 for a discussion of the issues found here with
|
||||
// a previously existing assert, and the likely reasons for things going wrong here.
|
||||
//
|
||||
// TL;DR: There may be a threading issue, or it could be due to something lacking joints, which would cause
|
||||
// initJointStates() to fail to make _rig.jointStatesEmpty() false, causing things to end up here twice.
|
||||
//
|
||||
// In any case it appears to be safe to simply clear _meshStates here, even though this shouldn't happen.
|
||||
_meshStates.clear();
|
||||
qCWarning(renderutils) << "_meshStates has" << _meshStates.size() << "items when it should have none. Model with URL "
|
||||
<< _url.toString() << "; translation" << _translation << "; rotation" << _rotation << "; scale" << _scale
|
||||
<< "; joint state count" << _rig.getJointStateCount() << "; type" << (modelProviderType == NestableType::Avatar ? "Avatar" : "Entity");
|
||||
}
|
||||
|
||||
const HFMModel& hfmModel = getHFMModel();
|
||||
int i = 0;
|
||||
|
@ -841,8 +854,8 @@ void Model::calculateTriangleSets(const HFMModel& hfmModel) {
|
|||
int i2 = part.quadIndices[vIndex++];
|
||||
int i3 = part.quadIndices[vIndex++];
|
||||
|
||||
// track the model space version... these points will be transformed by the FST's offset,
|
||||
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||
// track the model space version... these points will be transformed by the FST's offset,
|
||||
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||
// this can't change at runtime, so we can safely store these in our TriangleSet
|
||||
glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||
|
@ -863,8 +876,8 @@ void Model::calculateTriangleSets(const HFMModel& hfmModel) {
|
|||
int i1 = part.triangleIndices[vIndex++];
|
||||
int i2 = part.triangleIndices[vIndex++];
|
||||
|
||||
// track the model space version... these points will be transformed by the FST's offset,
|
||||
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||
// track the model space version... these points will be transformed by the FST's offset,
|
||||
// which includes the scaling, rotation, and translation specified by the FST/FBX,
|
||||
// this can't change at runtime, so we can safely store these in our TriangleSet
|
||||
glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// libraries/shared/src/shared/
|
||||
//
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -11,31 +12,38 @@
|
|||
#ifndef hifi_WebRTC_h
|
||||
#define hifi_WebRTC_h
|
||||
|
||||
#ifndef QSYSTEMDETECTION_H
|
||||
#include <qsystemdetection.h>
|
||||
#endif
|
||||
|
||||
// WEBRTC_AUDIO: WebRTC audio features, e.g., echo canceling.
|
||||
// WEBRTC_DATA_CHANNELS: WebRTC client-server connections in parallel with UDP.
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_POSIX 1
|
||||
# define WEBRTC_LEGACY 1
|
||||
#elif defined(Q_OS_WIN)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_DATA_CHANNELS 1
|
||||
# define WEBRTC_WIN 1
|
||||
# define NOMINMAX 1
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
// I don't yet have a working libwebrtc for android
|
||||
// # define WEBRTC_ENABLED 1
|
||||
// # define WEBRTC_AUDIO 1
|
||||
// # define WEBRTC_POSIX 1
|
||||
// # define WEBRTC_LEGACY 1
|
||||
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_POSIX 1
|
||||
# define WEBRTC_DATA_CHANNELS 1
|
||||
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM)
|
||||
// WebRTC is basically impossible to build on aarch64 Linux.
|
||||
// I am looking at https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing for an alternative.
|
||||
// # define WEBRTC_ENABLED 1
|
||||
// # define WEBRTC_AUDIO 1
|
||||
// # define WEBRTC_POSIX 1
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
# include <modules/audio_processing/include/audio_processing.h>
|
||||
# include "modules/audio_processing/audio_processing_impl.h"
|
||||
// # define WEBRTC_LEGACY 1
|
||||
#endif
|
||||
|
||||
#endif // hifi_WebRTC_h
|
||||
|
|
17
prebuild.py
17
prebuild.py
|
@ -58,22 +58,6 @@ class TrackableLogger(logging.Logger):
|
|||
logging.setLoggerClass(TrackableLogger)
|
||||
logger = logging.getLogger('prebuild')
|
||||
|
||||
def headSha():
|
||||
if shutil.which('git') is None:
|
||||
logger.warn("Unable to find git executable, can't caclulate commit ID")
|
||||
return '0xDEADBEEF'
|
||||
repo_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
git = subprocess.Popen(
|
||||
'git rev-parse --short HEAD',
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
shell=True, cwd=repo_dir, universal_newlines=True,
|
||||
)
|
||||
stdout, _ = git.communicate()
|
||||
sha = stdout.split('\n')[0]
|
||||
if not sha:
|
||||
raise RuntimeError("couldn't find git sha for repository {}".format(repo_dir))
|
||||
return sha
|
||||
|
||||
@contextmanager
|
||||
def timer(name):
|
||||
''' Print the elapsed time a context's execution takes to execute '''
|
||||
|
@ -120,7 +104,6 @@ def main():
|
|||
if args.ci_build:
|
||||
logging.basicConfig(datefmt='%H:%M:%S', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO)
|
||||
|
||||
logger.info('sha=%s' % headSha())
|
||||
logger.info('start')
|
||||
|
||||
# OS dependent information
|
||||
|
|
24
screenshare/package-lock.json
generated
24
screenshare/package-lock.json
generated
|
@ -71,9 +71,9 @@
|
|||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.19.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz",
|
||||
"integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==",
|
||||
"version": "12.20.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.29.tgz",
|
||||
"integrity": "sha512-dU2ypz+gO5va1OBvs0iT3BNHG5SgTqRvq8r+kU3e/LAseKapUJ8zTUE9Ve9fTpi27tN/7ahOAhCJwQWsffvsyw==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
|
@ -162,9 +162,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cacheable-request": {
|
||||
|
@ -298,9 +298,9 @@
|
|||
}
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-zip": {
|
||||
|
@ -365,9 +365,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"electron": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz",
|
||||
"integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==",
|
||||
"version": "11.5.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-11.5.0.tgz",
|
||||
"integrity": "sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/highfidelity/hifi#readme",
|
||||
"devDependencies": {
|
||||
"electron": "^9.4.0",
|
||||
"electron": "^11.5.0",
|
||||
"electron-packager": "^14.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -203,8 +203,8 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
var cameraPosition = Camera.position;
|
||||
PROFILE("getMultipleProperties", function () {
|
||||
var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked',
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'certificateID',
|
||||
'skybox.url', 'ambientLight.url', 'created', 'lastEdited']);
|
||||
'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'serverScripts',
|
||||
'certificateID', 'skybox.url', 'ambientLight.url', 'created', 'lastEdited']);
|
||||
for (var i = 0; i < multipleProperties.length; i++) {
|
||||
var properties = multipleProperties[i];
|
||||
|
||||
|
@ -247,7 +247,7 @@ EntityListTool = function(shouldUseEditTabletApp) {
|
|||
isBaked: entityIsBaked(properties),
|
||||
drawCalls: (properties.renderInfo !== undefined ?
|
||||
valueIfDefined(properties.renderInfo.drawCalls) : ""),
|
||||
hasScript: properties.script !== "",
|
||||
hasScript: (properties.script !== "" || properties.serverScripts !== ""),
|
||||
parentState: parentState,
|
||||
created: formatToStringDateTime(properties.created),
|
||||
lastEdited: formatToStringDateTime(properties.lastEdited)
|
||||
|
|
309
scripts/system/inventory/package-lock.json
generated
309
scripts/system/inventory/package-lock.json
generated
|
@ -158,12 +158,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -204,12 +198,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -239,12 +227,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -357,12 +339,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -461,12 +437,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -590,12 +560,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -644,12 +608,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -678,12 +636,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -712,12 +664,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -798,12 +744,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -832,12 +772,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -854,14 +788,6 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-remap-async-to-generator": {
|
||||
|
@ -987,12 +913,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1118,12 +1038,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1190,12 +1104,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1336,12 +1244,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1466,12 +1368,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1859,12 +1755,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2007,12 +1897,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2142,12 +2026,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2344,12 +2222,6 @@
|
|||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2972,9 +2844,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"ssri": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz",
|
||||
"integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz",
|
||||
"integrity": "sha512-w+daCzXN89PseTL99MkA+fxJEcU3wfaE/ah0i0lnOlpG1CYLJ2ZjzEry68YBKfLs4JfoTShrTEsJkAZuNZ/stw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"figgy-pudding": "^3.5.1",
|
||||
|
@ -3976,15 +3848,48 @@
|
|||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz",
|
||||
"integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==",
|
||||
"version": "4.17.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.6.tgz",
|
||||
"integrity": "sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001093",
|
||||
"electron-to-chromium": "^1.3.488",
|
||||
"escalade": "^3.0.1",
|
||||
"node-releases": "^1.1.58"
|
||||
"caniuse-lite": "^1.0.30001274",
|
||||
"electron-to-chromium": "^1.3.886",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^2.0.1",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001280",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001280.tgz",
|
||||
"integrity": "sha512-kFXwYvHe5rix25uwueBxC569o53J6TpnGu0BEEn+6Lhl2vsnAumRFWEBhDft1fwyo6m1r4i+RqA4+163FpeFcA==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.896",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.896.tgz",
|
||||
"integrity": "sha512-NcGkBVXePiuUrPLV8IxP43n1EOtdg+dudVjrfVEUd/bOqpQUFZ2diL5PPYzbgEhZFEltdXV3AcyKwGnEQ5lhMA==",
|
||||
"dev": true
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
|
||||
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
|
@ -4570,9 +4475,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
|
||||
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz",
|
||||
"integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
|
@ -5563,9 +5468,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"dns-packet": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
|
||||
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
|
||||
"integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ip": "^1.1.0",
|
||||
|
@ -5715,31 +5620,25 @@
|
|||
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.502",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.502.tgz",
|
||||
"integrity": "sha512-TIeXOaHAvfP7FemGUtAJxStmOc1YFGWFNqdey/4Nk41L9b1nMmDVDGNMIWhZJvOfJxix6Cv5FGEnBK+yvw3UTg==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
"hmac-drbg": "^1.0.1",
|
||||
"inherits": "^2.0.4",
|
||||
"minimalistic-assert": "^1.0.1",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.9",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -5857,12 +5756,6 @@
|
|||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
|
||||
"integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
|
@ -6965,9 +6858,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.18.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz",
|
||||
"integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==",
|
||||
"version": "9.18.5",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.5.tgz",
|
||||
"integrity": "sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==",
|
||||
"dev": true
|
||||
},
|
||||
"hmac-drbg": {
|
||||
|
@ -6988,9 +6881,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"hpack.js": {
|
||||
|
@ -8109,9 +8002,9 @@
|
|||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.defaultsdeep": {
|
||||
|
@ -8671,12 +8564,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.59",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.59.tgz",
|
||||
"integrity": "sha512-H3JrdUczbdiwxN5FuJPyCHnGHIFqQ0wWxo+9j1kAXAzqNMAHlo+4I/sYYxpyK0irQ73HgdiyzD32oqQDcU2Osw==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
|
@ -9181,9 +9068,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
|
@ -9228,6 +9115,12 @@
|
|||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
|
||||
"integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
|
@ -9302,14 +9195,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"version": "7.0.39",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
|
||||
"integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^6.1.0"
|
||||
"picocolors": "^0.2.1",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
|
@ -9317,15 +9209,6 @@
|
|||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -11172,9 +11055,9 @@
|
|||
}
|
||||
},
|
||||
"ssri": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
|
||||
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
|
||||
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"figgy-pudding": "^3.5.1"
|
||||
|
@ -11948,9 +11831,9 @@
|
|||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
|
||||
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
|
||||
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
|
@ -12886,9 +12769,9 @@
|
|||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
|
@ -12901,9 +12784,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
If you want to run Doxygen from a command prompt, add the Doxygen install's `/bin` directory to your system PATH.
|
||||
|
||||
|
||||
## Documenting
|
||||
|
||||
See section 4.3.6 of the [*Coding Standard*](../../CODING_STANDARD.md).
|
||||
|
||||
|
||||
## Running
|
||||
|
||||
### Using the Doxywizard GUI
|
||||
|
|
|
@ -64,7 +64,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
|
|||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
|
||||
}
|
||||
|
||||
_stunSockAddr = SockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true);
|
||||
_stunSockAddr = SockAddr(SocketType::UDP, STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true);
|
||||
|
||||
_cacheSTUNResult = parser.isSet(cacheSTUNOption);
|
||||
|
||||
|
@ -81,7 +81,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
|
|||
}
|
||||
}
|
||||
|
||||
_iceServerAddr = SockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT);
|
||||
_iceServerAddr = SockAddr(SocketType::UDP, "127.0.0.1", ICE_SERVER_DEFAULT_PORT);
|
||||
if (parser.isSet(iceServerAddressOption)) {
|
||||
// parse the IP and port combination for this target
|
||||
QString hostnamePortString = parser.value(iceServerAddressOption);
|
||||
|
@ -98,7 +98,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
|
|||
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
} else {
|
||||
_iceServerAddr = SockAddr(address, port);
|
||||
_iceServerAddr = SockAddr(SocketType::UDP, address, port);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ void ICEClientApp::openSocket() {
|
|||
|
||||
_socket = new udt::Socket();
|
||||
unsigned int localPort = 0;
|
||||
_socket->bind(QHostAddress::AnyIPv4, localPort);
|
||||
_socket->bind(SocketType::UDP, QHostAddress::AnyIPv4, localPort);
|
||||
_socket->setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
|
||||
_socket->addUnfilteredHandler(_stunSockAddr,
|
||||
[this](std::unique_ptr<udt::BasePacket> packet) {
|
||||
|
@ -142,10 +142,10 @@ void ICEClientApp::openSocket() {
|
|||
});
|
||||
|
||||
if (_verbose) {
|
||||
qDebug() << "local port is" << _socket->localPort();
|
||||
qDebug() << "local port is" << _socket->localPort(SocketType::UDP);
|
||||
}
|
||||
_localSockAddr = SockAddr("127.0.0.1", _socket->localPort());
|
||||
_publicSockAddr = SockAddr("127.0.0.1", _socket->localPort());
|
||||
_localSockAddr = SockAddr(SocketType::UDP, "127.0.0.1", _socket->localPort(SocketType::UDP));
|
||||
_publicSockAddr = SockAddr(SocketType::UDP, "127.0.0.1", _socket->localPort(SocketType::UDP));
|
||||
_domainPingCount = 0;
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ void ICEClientApp::doSomething() {
|
|||
if (_verbose) {
|
||||
qDebug() << "using cached STUN response";
|
||||
}
|
||||
_publicSockAddr.setPort(_socket->localPort());
|
||||
_publicSockAddr.setPort(_socket->localPort(SocketType::UDP));
|
||||
setState(talkToIceServer);
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ void ICEClientApp::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet)
|
|||
uint16_t newPublicPort;
|
||||
QHostAddress newPublicAddress;
|
||||
if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) {
|
||||
_publicSockAddr = SockAddr(newPublicAddress, newPublicPort);
|
||||
_publicSockAddr = SockAddr(SocketType::UDP, newPublicAddress, newPublicPort);
|
||||
if (_verbose) {
|
||||
qDebug() << "My public address is" << _publicSockAddr;
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d2e383cab811018422433fe0d8f224e53e0506cf
|
||||
Subproject commit 0afaa769d46683d461c9288aa31468f64cba0233
|
Loading…
Reference in a new issue