Merge branch 'master' into feature/update-shortcut-names

This commit is contained in:
Kalila L 2020-10-02 15:24:18 -04:00
commit 11d847b74b
366 changed files with 329539 additions and 11901 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,7 @@ macro(SET_PACKAGING_PARAMETERS)
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
set_from_env(STABLE_BUILD STABLE_BUILD 0)
set_from_env(INITIAL_STARTUP_LOCATION INITIAL_STARTUP_LOCATION "")
message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}")

View file

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

View file

@ -636,6 +636,7 @@ Var Express
; figure out where to download installer slideshow images from
StrCpy $0 "http://cdn.highfidelity.com/installer/slideshow"
; Is this in use anymore?
${If} $CampaignName == ""
StrCpy $0 "$0/default"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,12 +3,12 @@
<h4 class="step-title"></h4>
<dl class="row">
<dd class="col-md-12">
<span class='step-description'>By connecting your High Fidelity Account you will be granting access to your account information.</span>
<span class='step-description'>By connecting your Metaverse Account you will be granting access to your account information.</span>
</dd>
</dl>
<dl class="row">
<dd class="col-md-12">
<a id="connect-account-btn" role="button" class="btn btn-primary btn-md btn-block" target="_blank">Connect your High Fidelity account</a>
<a id="connect-account-btn" role="button" class="btn btn-primary btn-md btn-block" target="_blank">Connect your Metaverse account</a>
</dd>
</dl>
@ -25,7 +25,7 @@
<div class="col-md-12">
<span class='step-description'>
<a target='_blank' href='https://docs.vircadia.dev/create-and-explore/start-working-in-your-sandbox/place-names'>Place names</a> are similar to web addresses. Users who want to visit your domain can
enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.</br>
enter its Place Name in Vircadia's Interface. You can choose a Place Name for your domain.</br>
Your domain may also be reachable by <b>IP address</b>.
</span>
</div>
@ -56,11 +56,11 @@
<div class="wizard-step col-md-9 col-centered" style="display: none;">
<h4 class="step-title"></h4>
<div class="row">
<p id="permissions-description" class="col-md-12 step-info"><b>Localhost</b> has been granted administrator privileges to this domain. (Localhost is any</br>user on the same machine as the High Fidelity Server)</p>
<p id="permissions-description" class="col-md-12 step-info"><b>Localhost</b> has been granted administrator privileges to this domain. (Localhost is any</br>user on the same machine as the Vircadia Server)</p>
</div>
<div id="admin-row" class="row">
<p class="col-md-6">
<span class="step-info"><span id="admin-description"><b>Add your High Fidelity username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
<span class="step-info"><span id="admin-description"><b>Add your Metaverse username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
</p>
<div class="col-md-6">
<input id="admin-usernames" type="text" class="form-control">
@ -95,7 +95,7 @@
</p>
<p class="col-md-5">
<label>
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into High Fidelity
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into the Metaverse
</label>
</p>
<p class="col-md-2">
@ -126,7 +126,7 @@
</p>
<p class="col-md-5">
<label>
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into High Fidelity
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into the Metaverse
</label>
</p>
<p class="col-md-2">
@ -164,14 +164,14 @@
<dt class="col-md-4 step-info">Username</dt>
<dd class="col-md-8">
<input id="http_username" type="text" class="form-control">
<span class='help-block'>This does not have to be your High Fidelity username</span>
<span class='help-block'>This does not have to be your Metaverse username</span>
</dd>
</dl>
<dl class="row">
<dt class="col-md-4 step-info">Enter password</dt>
<dd class="col-md-8">
<input id="http_password" type="password" class="form-control">
<span class='help-block'>This should not be the same as your High Fidelity password</span>
<span class='help-block'>This should not be the same as your Metaverse password</span>
</dd>
</dl>
<dl id="verify-password-row" class="row">
@ -262,4 +262,5 @@
<script src='/js/bootbox.min.js'></script>
<script src='/js/sha256.js'></script>
<script src='js/wizard.js'></script>
<script src='../js/shared.js'></script>
<!--#include virtual="page-end.html"-->

View file

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

View file

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

View file

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

View file

@ -15,8 +15,9 @@
#include <QVariantMap>
#include <QJsonObject>
#include "HTTPManager.h"
class DomainMetadata : public QObject {
class DomainMetadata : public QObject, public HTTPRequestHandler {
Q_OBJECT
public:
@ -33,25 +34,29 @@ public:
static const QString DESCRIPTORS;
class Descriptors {
public:
static const QString NAME;
static const QString DESCRIPTION;
static const QString THUMBNAIL;
static const QString IMAGES;
static const QString CAPACITY;
static const QString RESTRICTION;
static const QString MATURITY;
static const QString HOSTS;
static const QString CONTACT;
static const QString MANAGERS;
static const QString TAGS;
};
DomainMetadata(QObject* domainServer);
DomainMetadata() = delete;
~DomainMetadata() = default;
// Get cached metadata
QJsonObject get();
QJsonObject get(const QString& group);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;
public slots:
void descriptorsChanged();
void securityChanged(bool send);
void securityChanged() { securityChanged(true); }
void usersChanged();
protected:

View file

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

View file

@ -140,6 +140,7 @@ private slots:
void updateDownstreamNodes();
void updateUpstreamNodes();
void initializeExporter();
void initializeMetadataExporter();
void tokenGrantFinished();
void profileRequestFinished();
@ -240,6 +241,8 @@ private:
HTTPManager _httpManager;
HTTPManager* _httpExporterManager { nullptr };
HTTPManager* _httpMetadataExporterManager { nullptr };
std::unique_ptr<HTTPSManager> _httpsManager;
QHash<QUuid, SharedAssignmentPointer> _allAssignments;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -253,6 +253,7 @@
#include <DesktopPreviewProvider.h>
#include "AboutUtil.h"
#include "ExternalResource.h"
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
@ -1352,10 +1353,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(),
&DialogsManager::showDomainLoginDialog);
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this,
&Application::updateWindowTitle);
connect(domainAccountManager.data(), &DomainAccountManager::authRequired,
dialogsManager.data(), &DialogsManager::showDomainLoginDialog);
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, this, &Application::updateWindowTitle);
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &Application::updateWindowTitle);
// ####### TODO: Connect any other signals from domainAccountManager.
// use our MyAvatar position and quat for address manager path
@ -3512,8 +3513,10 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Selection", DependencyManager::get<SelectionScriptingInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("About", AboutUtil::getInstance());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -3623,10 +3626,13 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
surfaceContext->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
surfaceContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
surfaceContext->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance());
surfaceContext->setContextProperty("About", AboutUtil::getInstance());
surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Deprecated.
surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
surfaceContext->setContextProperty("PlatformInfo", PlatformInfoScriptingInterface::getInstance());
surfaceContext->setContextProperty("ExternalResource", ExternalResource::getInstance());
// This `module` context property is blank for the QML scripting interface so that we don't get log errors when importing
// certain JS files from both scripts (in the JS context) and QML (in the QML context).
surfaceContext->setContextProperty("module", "");
@ -3974,6 +3980,11 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (_firstRun.get()) {
if (!BuildInfo::INITIAL_STARTUP_LOCATION.isEmpty()) {
DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(NetworkingConstants::DEFAULT_VIRCADIA_ADDRESS);
Menu::getInstance()->triggerOption(MenuOption::HomeLocation);
}
if (!_overrideEntry) {
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
@ -5532,6 +5543,19 @@ void Application::loadSettings() {
}
getMyAvatar()->loadData();
auto bucketEnum = QMetaEnum::fromType<ExternalResource::Bucket>();
auto externalResource = ExternalResource::getInstance();
for (int i = 0; i < bucketEnum.keyCount(); i++) {
const char* keyName = bucketEnum.key(i);
QString setting("ExternalResource/");
setting += keyName;
auto bucket = static_cast<ExternalResource::Bucket>(bucketEnum.keyToValue(keyName));
Setting::Handle<QString> url(setting, externalResource->getBase(bucket));
externalResource->setBase(bucket, url.get());
}
_settingsLoaded = true;
}
@ -5548,6 +5572,22 @@ void Application::saveSettings() const {
Menu::getInstance()->saveSettings();
getMyAvatar()->saveData();
PluginManager::getInstance()->saveSettings();
// Don't save external resource paths until such time as there's UI to select or set alternatives. Otherwise new default
// values won't be used unless Interface.json entries are manually remove or Interface.json is deleted.
/*
auto bucketEnum = QMetaEnum::fromType<ExternalResource::Bucket>();
auto externalResource = ExternalResource::getInstance();
for (int i = 0; i < bucketEnum.keyCount(); i++) {
const char* keyName = bucketEnum.key(i);
QString setting("ExternalResource/");
setting += keyName;
auto bucket = static_cast<ExternalResource::Bucket>(bucketEnum.keyToValue(keyName));
Setting::Handle<QString> url(setting, externalResource->getBase(bucket));
url.set(externalResource->getBase(bucket));
}
*/
}
bool Application::importEntities(const QString& urlOrFilename, const bool isObservable, const qint64 callerId) {
@ -7084,8 +7124,9 @@ void Application::updateWindowTitle() const {
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
bool isMetaverseLoggedIn = accountManager->isLoggedIn();
bool hasDomainLogIn = domainAccountManager->hasLogIn();
bool isDomainLoggedIn = domainAccountManager->isLoggedIn();
QString authedDomain = domainAccountManager->getAuthedDomain();
QString authedDomainName = domainAccountManager->getAuthedDomainName();
QString buildVersion = " - Vircadia - "
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
@ -7102,9 +7143,9 @@ void Application::updateWindowTitle() const {
QString currentPlaceName;
if (isServerlessMode()) {
if (isInErrorState) {
currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
currentPlaceName = "Serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString();
} else {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
currentPlaceName = "Serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
}
} else {
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
@ -7115,20 +7156,23 @@ void Application::updateWindowTitle() const {
QString metaverseDetails;
if (isMetaverseLoggedIn) {
metaverseDetails = "Metaverse: Logged in as " + metaverseUsername;
metaverseDetails = " (Metaverse: Connected to " + MetaverseAPI::getCurrentMetaverseServerURL().toString() + " as " + metaverseUsername + ")";
} else {
metaverseDetails = "Metaverse: Not Logged In";
metaverseDetails = " (Metaverse: Not Logged In)";
}
QString domainDetails;
if (currentPlaceName == authedDomain && isDomainLoggedIn) {
domainDetails = "Domain: Logged in as " + domainUsername;
if (hasDomainLogIn) {
if (currentPlaceName == authedDomainName && isDomainLoggedIn) {
domainDetails = " (Domain: Logged in as " + domainUsername + ")";
} else {
domainDetails = " (Domain: Not Logged In)";
}
} else {
domainDetails = "Domain: Not Logged In";
domainDetails = "";
}
QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")"
+ buildVersion;
QString title = currentPlaceName + connectionStatus + metaverseDetails + domainDetails + buildVersion;
#ifndef WIN32
// crashes with vs2013/win32
@ -7520,7 +7564,8 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get<WalletScriptingInterface>().data());
scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get<AddressManager>().data());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("About", AboutUtil::getInstance());
scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); // Deprecated.
scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get<ResourceRequestObserver>().data());
registerInteractiveWindowMetaType(scriptEngine.data());
@ -7657,7 +7702,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
QUrl scriptURL { scriptFilenameOrURL };
if (scriptURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) {
if (scriptURL.host().endsWith(NetworkingConstants::HF_MARKETPLACE_CDN_HOSTNAME)) {
int startIndex = shortName.lastIndexOf('/') + 1;
int endIndex = shortName.lastIndexOf('?');
shortName = shortName.mid(startIndex, endIndex - startIndex);
@ -7780,7 +7825,7 @@ bool Application::askToReplaceDomainContent(const QString& url) {
const int MAX_CHARACTERS_PER_LINE = 90;
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
QUrl originURL { url };
if (originURL.host().endsWith(NetworkingConstants::MARKETPLACE_CDN_HOSTNAME)) {
if (originURL.host().endsWith(NetworkingConstants::HF_MARKETPLACE_CDN_HOSTNAME)) {
// Create a confirmation dialog when this call is made
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -381,6 +381,7 @@ int main(int argc, const char* argv[]) {
#if defined(Q_OS_LINUX)
app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/vircadia-logo.svg"));
#endif
startCrashHookMonitor(&app);
QTimer exitTimer;
if (traceDuration > 0.0f) {

View file

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

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@
//
// Created by Clement on 3/17/14.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -32,8 +33,8 @@
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" };
static const QString S3_URL = "http://s3.amazonaws.com/hifi-public";
static const QString PUBLIC_URL = "http://public.highfidelity.io";
static const QString S3_URL = NetworkingConstants::HF_PUBLIC_CDN_URL;
static const QString PUBLIC_URL = "http://public.vircadia.com"; // Changed to Vircadia but not entirely sure what to do with this yet.
static const QString MODELS_LOCATION = "models/";
static const QString PREFIX_PARAMETER_NAME = "prefix";

View file

@ -424,6 +424,7 @@ void OctreeStatsDialog::showOctreeServersOfType(NodeType_t serverType) {
extraDetails << "<br/>" << itemInfo.caption << " " << stats.getItemValue(item);
}
} // fall through... since MOST has all of MORE
/* fall-thru */
case MORE: {
QString totalString = locale.toString((uint)stats.getTotalElements());
QString internalString = locale.toString((uint)stats.getTotalInternal());

View file

@ -458,6 +458,8 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
// LOD Details
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
STAT_UPDATE(numEntityUpdates, DependencyManager::get<EntityTreeRenderer>()->getPrevNumEntityUpdates());
STAT_UPDATE(numNeededEntityUpdates, DependencyManager::get<EntityTreeRenderer>()->getPrevTotalNeededEntityUpdates());
}

View file

@ -248,6 +248,10 @@ private: \
* <em>Read-only.</em>
* @property {string} lodStatus - Description of the current LOD.
* <em>Read-only.</em>
* @property {string} numEntityUpdates - The number of entity updates that happened last frame.
* <em>Read-only.</em>
* @property {string} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
* <em>Read-only.</em>
* @property {string} timingStats - Details of the average time (ms) spent in and number of calls made to different parts of
* the code. Provided only if <code>timingExpanded</code> is <code>true</code>. Only the top 10 items are provided if
* Developer &gt; Timing &gt; Performance Timer &gt; Only Display Top 10 is enabled.
@ -543,6 +547,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, lodAngle, 0)
STATS_PROPERTY(int, lodTargetFramerate, 0)
STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(int, numEntityUpdates, 0)
STATS_PROPERTY(int, numNeededEntityUpdates, 0)
STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, gameUpdateStats, QString())
STATS_PROPERTY(int, serverElements, 0)
@ -1211,6 +1217,20 @@ signals:
*/
void lodStatusChanged();
/**jsdoc
* Triggered when the value of the <code>numEntityUpdates</code> property changes.
* @function Stats.numEntityUpdatesChanged
* @returns {Signal}
*/
void numEntityUpdatesChanged();
/**jsdoc
* Triggered when the value of the <code>numNeededEntityUpdates</code> property changes.
* @function Stats.numNeededEntityUpdatesChanged
* @returns {Signal}
*/
void numNeededEntityUpdatesChanged();
/**jsdoc
* Triggered when the value of the <code>timingStats</code> property changes.
* @function Stats.timingStatsChanged

View file

@ -455,7 +455,7 @@ public slots:
* @example <caption>Create an image overlay and report whether its image is loaded after 1s.</caption>
* var overlay = Overlays.addOverlay("image", {
* bounds: { x: 100, y: 100, width: 200, height: 200 },
* imageURL: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png"
* imageURL: "https://content.vircadia.com/eu-c-1/vircadia-assets/interface/default/default_particle.png"
* });
* Script.setTimeout(function () {
* var isLoaded = Overlays.isLoaded(overlay);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2072,7 +2072,6 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi
// NOTE: device start() uses the Qt internal device list
Lock lock(_deviceMutex);
Lock localAudioLock(_localAudioMutex);
_localSamplesAvailable.exchange(0, std::memory_order_release);
//wait on local injectors prep to finish running
@ -2080,6 +2079,8 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi
_localPrepInjectorFuture.waitForFinished();
}
Lock localAudioLock(_localAudioMutex);
// cleanup any previously initialized device
if (_audioOutput) {
_audioOutputIODevice.close();

View file

@ -157,7 +157,7 @@ void Head::simulate(float deltaTime) {
updateEyeLookAt();
}
// use data to update fake Faceshift blendshape coefficients
// Use data to update fake blendshape coefficients.
if (getProceduralAnimationFlag(HeadData::AudioProceduralBlendshapeAnimation) &&
!getSuppressProceduralAnimationFlag(HeadData::AudioProceduralBlendshapeAnimation)) {

View file

@ -425,6 +425,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
}
float expectedUpdateCost = _avgRenderableUpdateCost * _renderablesToUpdate.size();
_prevTotalNeededEntityUpdates = _renderablesToUpdate.size();
if (expectedUpdateCost < MAX_UPDATE_RENDERABLES_TIME_BUDGET) {
// we expect to update all renderables within available time budget
PROFILE_RANGE_EX(simulation_physics, "UpdateRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
@ -433,7 +434,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
assert(renderable); // only valid renderables are added to _renderablesToUpdate
renderable->updateInScene(scene, transaction);
}
size_t numRenderables = _renderablesToUpdate.size() + 1; // add one to avoid divide by zero
_prevNumEntityUpdates = _renderablesToUpdate.size();
size_t numRenderables = _prevNumEntityUpdates + 1; // add one to avoid divide by zero
_renderablesToUpdate.clear();
// compute average per-renderable update cost
@ -494,7 +496,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
}
// compute average per-renderable update cost
size_t numUpdated = sortedRenderables.size() - _renderablesToUpdate.size() + 1; // add one to avoid divide by zero
_prevNumEntityUpdates = sortedRenderables.size() - _renderablesToUpdate.size();
size_t numUpdated = _prevNumEntityUpdates + 1; // add one to avoid divide by zero
float cost = (float)(usecTimestampNow() - updateStart) / (float)(numUpdated);
const float BLEND = 0.1f;
_avgRenderableUpdateCost = (1.0f - BLEND) * _avgRenderableUpdateCost + BLEND * cost;

View file

@ -136,6 +136,9 @@ public:
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
int getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; }
int getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; }
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);
@ -249,6 +252,8 @@ private:
ReadWriteLockable _changedEntitiesGuard;
std::unordered_set<EntityItemID> _changedEntities;
int _prevNumEntityUpdates { 0 };
int _prevTotalNeededEntityUpdates { 0 };
std::unordered_set<EntityRendererPointer> _renderablesToUpdate;
std::unordered_map<EntityItemID, EntityRendererPointer> _entitiesInScene;

View file

@ -219,13 +219,6 @@ void EntityRenderer::render(RenderArgs* args) {
return;
}
if (!_renderUpdateQueued && needsRenderUpdate()) {
// FIXME find a way to spread out the calls to needsRenderUpdate so that only a given subset of the
// items checks every frame, like 1/N of the tree ever N frames
_renderUpdateQueued = true;
emit requestRenderUpdate();
}
if (_visible && (args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || !_cauterized)) {
doRender(args);
}
@ -344,11 +337,6 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
}
_updateTime = usecTimestampNow();
// FIXME is this excessive?
if (!needsRenderUpdate()) {
return;
}
doRenderUpdateSynchronous(scene, transaction, _entity);
transaction.updateItem<PayloadProxyInterface>(_renderItemID, [this](PayloadProxyInterface& self) {
if (!isValidRenderItem()) {
@ -356,7 +344,6 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
}
// Happens on the render thread. Classes should use
doRenderUpdateAsynchronous(_entity);
_renderUpdateQueued = false;
});
}
@ -457,7 +444,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
}
void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
QObject::connect(this, &EntityRenderer::requestRenderUpdate, this, [this] {
QObject::connect(this, &EntityRenderer::requestRenderUpdate, this, [this] {
auto renderer = DependencyManager::get<EntityTreeRenderer>();
if (renderer) {
renderer->onEntityChanged(_entity->getID());
@ -466,7 +453,10 @@ void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
_changeHandlerId = entity->registerChangeHandler([](const EntityItemID& changedEntity) {
auto renderer = DependencyManager::get<EntityTreeRenderer>();
if (renderer) {
renderer->onEntityChanged(changedEntity);
auto renderable = renderer->renderableForEntityId(changedEntity);
if (renderable && renderable->needsRenderUpdate()) {
renderer->onEntityChanged(changedEntity);
}
}
});
}

View file

@ -148,15 +148,12 @@ protected:
QVector<QUuid> _renderWithZones;
bool _cauterized { false };
bool _moving { false };
// Only touched on the rendering thread
bool _renderUpdateQueued{ false };
Transform _renderTransform;
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;
quint64 _created;
QUuid _entityID;
// The base class relies on comparing the model transform to the entity transform in order
// to trigger an update, so the member must not be visible to derived classes as a modifiable
@ -166,6 +163,8 @@ protected:
// i.e. to see if the rendering code needs to update because of a change in state of the
// entity. This forces all the rendering code itself to be independent of the entity
const EntityItemPointer _entity;
QUuid _entityID;
};
template <typename T>
@ -191,10 +190,7 @@ protected:
using Parent::needsRenderUpdateFromEntity;
// Returns true if the item in question needs to have updateInScene called because of changes in the entity
virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final {
if (Parent::needsRenderUpdateFromEntity(entity)) {
return true;
}
return needsRenderUpdateFromTypedEntity(_typedEntity);
return Parent::needsRenderUpdateFromEntity(entity) || needsRenderUpdateFromTypedEntity(_typedEntity);
}
virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) override final {

View file

@ -39,6 +39,16 @@ bool GizmoEntityRenderer::isTransparent() const {
return Parent::isTransparent() || ringTransparent;
}
void GizmoEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
});
});
}
void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
bool dirty = false;
RingGizmoPropertyGroup ringProperties = entity->getRingProperties();
@ -186,15 +196,6 @@ void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
}
}
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
withWriteLock([&] {
updateModelTransformAndBound();
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
});
});
}
Item::Bound GizmoEntityRenderer::getBound() {

View file

@ -29,6 +29,7 @@ protected:
bool isTransparent() const override;
private:
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity);
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;

View file

@ -41,10 +41,9 @@ void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
});
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
_dimensions = entity->getScaledDimensions();
updateModelTransformAndBound();
_renderTransform = getModelTransform();
});
});

View file

@ -30,11 +30,9 @@ bool ImageEntityRenderer::isTransparent() const {
}
bool ImageEntityRenderer::needsRenderUpdate() const {
bool textureLoadedChanged = resultWithReadLock<bool>([&] {
return (!_textureIsLoaded && _texture && _texture->isLoaded());
});
if (textureLoadedChanged) {
if (resultWithReadLock<bool>([&] {
return !_textureIsLoaded;
})) {
return true;
}
@ -63,15 +61,15 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
_pulseProperties = entity->getPulseProperties();
_billboardMode = entity->getBillboardMode();
if (!_textureIsLoaded && _texture && _texture->isLoaded()) {
_textureIsLoaded = true;
if (!_textureIsLoaded) {
emit requestRenderUpdate();
}
_textureIsLoaded = _texture && (_texture->isLoaded() || _texture->isFailed());
});
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
updateModelTransformAndBound();
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
});

View file

@ -1460,6 +1460,31 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
emit requestRenderUpdate();
}
if (!_allProceduralMaterialsLoaded) {
std::lock_guard<std::mutex> lock(_materialsLock);
bool allProceduralMaterialsLoaded = true;
for (auto& shapeMaterialPair : _materials) {
auto material = shapeMaterialPair.second;
while (!material.empty()) {
auto mat = material.top();
if (mat.material && mat.material->isProcedural() && !mat.material->isReady()) {
allProceduralMaterialsLoaded = false;
break;
}
material.pop();
}
if (!allProceduralMaterialsLoaded) {
break;
}
}
if (!allProceduralMaterialsLoaded) {
emit requestRenderUpdate();
} else {
_allProceduralMaterialsLoaded = true;
model->setRenderItemsNeedUpdate();
}
}
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
// we will watch for that and ask the model to update it's render items
if (model->getRenderItemsNeedUpdate()) {
@ -1584,6 +1609,10 @@ void ModelEntityRenderer::addMaterial(graphics::MaterialLayer material, const st
if (_model && _model->fetchRenderItemIDs().size() > 0) {
_model->addMaterial(material, parentMaterialName);
}
if (material.material && material.material->isProcedural()) {
_allProceduralMaterialsLoaded = false;
emit requestRenderUpdate();
}
}
void ModelEntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {

View file

@ -202,10 +202,10 @@ private:
bool _prevModelLoaded { false };
void processMaterials();
bool _allProceduralMaterialsLoaded { false };
static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs);
};
} } // namespace

View file

@ -64,6 +64,16 @@ ParticleEffectEntityRenderer::ParticleEffectEntityRenderer(const EntityItemPoint
});
}
bool ParticleEffectEntityRenderer::needsRenderUpdate() const {
if (resultWithReadLock<bool>([&] {
return !_textureLoaded;
})) {
return true;
}
return Parent::needsRenderUpdate();
}
void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
auto newParticleProperties = entity->getParticleProperties();
if (!newParticleProperties.valid()) {
@ -102,6 +112,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
}
withWriteLock([&] {
_textureLoaded = true;
entity->setVisuallyReady(true);
});
} else {
@ -111,20 +122,29 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
if (textureNeedsUpdate) {
withWriteLock([&] {
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
_textureLoaded = false;
entity->setVisuallyReady(false);
});
}
if (_networkTexture) {
if (!_textureLoaded) {
emit requestRenderUpdate();
}
bool textureLoaded = resultWithReadLock<bool>([&] {
return _networkTexture && (_networkTexture->isLoaded() || _networkTexture->isFailed());
});
if (textureLoaded) {
withWriteLock([&] {
entity->setVisuallyReady(_networkTexture->isFailed() || _networkTexture->isLoaded());
entity->setVisuallyReady(true);
_textureLoaded = true;
});
}
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] {
withWriteLock([&] {
updateModelTransformAndBound();
_renderTransform = getModelTransform();
});
});
@ -146,7 +166,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
particleUniforms.rotateWithEntity = _particleProperties.rotateWithEntity ? 1 : 0;
});
// Update particle uniforms
memcpy(&_uniformBuffer.edit<ParticleUniforms>(), &particleUniforms, sizeof(ParticleUniforms));
_uniformBuffer.edit<ParticleUniforms>() = particleUniforms;
}
ItemKey ParticleEffectEntityRenderer::getKey() {

View file

@ -25,6 +25,7 @@ public:
ParticleEffectEntityRenderer(const EntityItemPointer& entity);
protected:
virtual bool needsRenderUpdate() const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
@ -111,6 +112,7 @@ private:
GeometryResource::Pointer _geometryResource;
NetworkTexturePointer _networkTexture;
bool _textureLoaded { false };
ScenePointer _scene;
};

View file

@ -204,9 +204,8 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged;
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] {
withWriteLock([&] {
updateModelTransformAndBound();
_renderTransform = getModelTransform();
if (geometryChanged) {

View file

@ -32,7 +32,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const {
if (resultWithReadLock<bool>([&] {
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() &&
mat->second.top().material->isEnabled()) {
mat->second.top().material->isReady()) {
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(mat->second.top().material);
if (procedural->isFading()) {
return true;
@ -70,27 +70,25 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
});
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
auto entity = getEntity();
_position = entity->getWorldPosition();
_dimensions = entity->getUnscaledDimensions(); // get unscaled to avoid scaling twice
_orientation = entity->getWorldOrientation();
updateModelTransformAndBound();
_renderTransform = getModelTransform(); // contains parent scale, if this entity scales with its parent
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
_renderTransform.postScale(_dimensions);
});;
});
});
}
void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
withReadLock([&] {
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) {
if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) {
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(mat->second.top().material);
if (procedural->isFading()) {
procedural->setIsFading(Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) < 1.0f);
@ -121,11 +119,16 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
materialChanged = true;
}
if (materialChanged) {
auto materials = _materials.find("0");
if (materials != _materials.end()) {
auto materials = _materials.find("0");
if (materials != _materials.end()) {
if (materialChanged) {
materials->second.setNeedsUpdate(true);
}
if (materials->second.shouldUpdate()) {
RenderPipelines::updateMultiMaterial(materials->second);
emit requestRenderUpdate();
}
}
});
}
@ -137,7 +140,7 @@ bool ShapeEntityRenderer::isTransparent() const {
auto mat = _materials.find("0");
if (mat != _materials.end() && mat->second.top().material) {
if (mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) {
if (mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) {
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(mat->second.top().material);
if (procedural->isFading()) {
return true;

View file

@ -102,10 +102,9 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
_dimensions = entity->getScaledDimensions();
updateModelTransformAndBound();
_renderTransform = getModelTransform();
_renderTransform.postScale(_dimensions);
});

View file

@ -1,6 +1,7 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -35,6 +36,9 @@ using namespace render::entities;
const QString WebEntityRenderer::QML = "Web3DSurface.qml";
const char* WebEntityRenderer::URL_PROPERTY = "url";
const char* WebEntityRenderer::SCRIPT_URL_PROPERTY = "scriptURL";
const char* WebEntityRenderer::GLOBAL_POSITION_PROPERTY = "globalPosition";
const char* WebEntityRenderer::USE_BACKGROUND_PROPERTY = "useBackground";
std::function<void(QString, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> WebEntityRenderer::_acquireWebSurfaceOperator = nullptr;
std::function<void(QSharedPointer<OffscreenQmlSurface>&, bool&, std::vector<QMetaObject::Connection>&)> WebEntityRenderer::_releaseWebSurfaceOperator = nullptr;
@ -100,7 +104,7 @@ WebEntityRenderer::~WebEntityRenderer() {
bool WebEntityRenderer::isTransparent() const {
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE || !_useBackground;
}
bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
@ -193,11 +197,15 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
if (_webSurface) {
if (_webSurface->getRootItem()) {
if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) {
if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) {
if (localSafeContext) {
::hifi::scripting::setLocalAccessSafeThread(true);
}
_webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL);
_webSurface->getRootItem()->setProperty(SCRIPT_URL_PROPERTY, _scriptURL);
_webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, _useBackground);
_webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(_contextPosition));
_webSurface->setMaxFps((QUrl(newSourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) ? YOUTUBE_MAX_FPS : _maxFPS);
::hifi::scripting::setLocalAccessSafeThread(false);
_sourceURL = newSourceURL;
} else if (_contentType != ContentType::HtmlContent) {
@ -207,7 +215,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
{
auto scriptURL = entity->getScriptURL();
if (_scriptURL != scriptURL) {
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
_webSurface->getRootItem()->setProperty(SCRIPT_URL_PROPERTY, scriptURL);
_scriptURL = scriptURL;
}
}
@ -226,21 +234,28 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
}
}
{
auto useBackground = entity->getUseBackground();
if (_useBackground != useBackground) {
_webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, useBackground);
_useBackground = useBackground;
}
}
{
auto contextPosition = entity->getWorldPosition();
if (_contextPosition != contextPosition) {
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(contextPosition));
_webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(contextPosition));
_contextPosition = contextPosition;
}
}
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] {
withWriteLock([&] {
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
updateModelTransformAndBound();
_renderTransform = getModelTransform();
_renderTransform.setScale(1.0f);
_renderTransform.postScale(entity->getScaledDimensions());
@ -291,12 +306,14 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
glm::vec4 color;
Transform transform;
bool forward;
bool transparent;
withReadLock([&] {
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
color = glm::vec4(toGlm(_color), _alpha * fadeRatio);
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
transform = _renderTransform;
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
transparent = isTransparent();
});
if (color.a == 0.0f) {
@ -310,7 +327,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
// Turn off jitter for these entities
batch.pushProjectionJitter();
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD, forward);
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, transparent, forward);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId);
batch.popProjectionJitter();
batch.setResourceTexture(0, nullptr);

View file

@ -1,6 +1,7 @@
//
// Created by Bradley Austin Davis on 2015/05/12
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -32,6 +33,9 @@ public:
static const QString QML;
static const char* URL_PROPERTY;
static const char* SCRIPT_URL_PROPERTY;
static const char* GLOBAL_POSITION_PROPERTY;
static const char* USE_BACKGROUND_PROPERTY;
static void setAcquireWebSurfaceOperator(std::function<void(const QString&, bool, QSharedPointer<OffscreenQmlSurface>&, bool&)> acquireWebSurfaceOperator) { _acquireWebSurfaceOperator = acquireWebSurfaceOperator; }
static void acquireWebSurface(const QString& url, bool htmlContent, QSharedPointer<OffscreenQmlSurface>& webSurface, bool& cachedWebSurface) {
@ -93,6 +97,7 @@ private:
uint16_t _dpi;
QString _scriptURL;
uint8_t _maxFPS;
bool _useBackground;
WebInputMode _inputMode;
glm::vec3 _contextPosition;

View file

@ -262,14 +262,6 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
entity->setVisuallyReady(visuallyReady);
}
void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
if (entity->getShapeType() == SHAPE_TYPE_SPHERE) {
_renderTransform = getModelTransform();
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
}
ItemKey ZoneEntityRenderer::getKey() {
return ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).build();
}
@ -306,8 +298,6 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
// FIXME: do we need to trigger an update when shapeType changes? see doRenderUpdateAsynchronousTyped
return false;
}

View file

@ -39,7 +39,6 @@ protected:
virtual void doRender(RenderArgs* args) override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
private:
void updateKeyZoneItemFromEntity(const TypedEntityPointer& entity);

View file

@ -617,10 +617,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
_lastEdited = lastEditedFromBufferAdjusted;
_lastEditedFromRemote = now;
_lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
// the properties out of the bitstream (see below))
somethingChangedNotification(); // notify derived classes that something has changed
}
// last updated is stored as ByteCountCoded delta from lastEdited
@ -1005,6 +1001,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
element->getTree()->trackIncomingEntityLastEdited(lastEditedFromBufferAdjusted, bytesRead);
}
if (somethingChanged) {
somethingChangedNotification();
}
return bytesRead;
}
@ -1573,14 +1572,14 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
qCDebug(entities) << "EntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
#endif
setLastEdited(now);
somethingChangedNotification(); // notify derived classes that something has changed
setLastEdited(properties._lastEdited);
if (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
// anything that sets the transform or velocity must update _lastSimulated which is used
// for kinematic extrapolation (e.g. we want to extrapolate forward from this moment
// when position and/or velocity was changed).
_lastSimulated = now;
}
somethingChangedNotification();
}
// timestamps
@ -1832,6 +1831,7 @@ void EntityItem::setPosition(const glm::vec3& value) {
void EntityItem::setParentID(const QUuid& value) {
QUuid oldParentID = getParentID();
if (oldParentID != value) {
_needsRenderUpdate = true;
EntityTreePointer tree = getTree();
if (tree && !oldParentID.isNull()) {
tree->removeFromChildrenOfAvatars(getThisPointer());
@ -3000,10 +3000,15 @@ bool EntityItem::getCauterized() const {
}
void EntityItem::setCauterized(bool value) {
bool needsRenderUpdate = false;
withWriteLock([&] {
_needsRenderUpdate |= _cauterized != value;
needsRenderUpdate = _cauterized != value;
_needsRenderUpdate |= needsRenderUpdate;
_cauterized = value;
});
if (needsRenderUpdate) {
somethingChangedNotification();
}
}
bool EntityItem::getIgnorePickIntersection() const {
@ -3042,10 +3047,15 @@ bool EntityItem::getCullWithParent() const {
}
void EntityItem::setCullWithParent(bool value) {
bool needsRenderUpdate = false;
withWriteLock([&] {
_needsRenderUpdate |= _cullWithParent != value;
needsRenderUpdate = _cullWithParent != value;
_needsRenderUpdate |= needsRenderUpdate;
_cullWithParent = value;
});
if (needsRenderUpdate) {
somethingChangedNotification();
}
}
bool EntityItem::isChildOfMyAvatar() const {

View file

@ -579,8 +579,8 @@ public:
bool stillHasMyGrab() const;
bool needsRenderUpdate() const { return resultWithReadLock<bool>([&] { return _needsRenderUpdate; }); }
void setNeedsRenderUpdate(bool needsRenderUpdate) { withWriteLock([&] { _needsRenderUpdate = needsRenderUpdate; }); }
bool needsRenderUpdate() const { return _needsRenderUpdate; }
void setNeedsRenderUpdate(bool needsRenderUpdate) { _needsRenderUpdate = needsRenderUpdate; }
void setRenderWithZones(const QVector<QUuid>& renderWithZones);
QVector<QUuid> getRenderWithZones() const;

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