From 3132cca482cb13c6f7d56b83fb88723779367e1b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 14 Jul 2016 14:49:03 -0700 Subject: [PATCH 01/34] prototype --- interface/resources/qml/AddressBarDialog.qml | 162 +++++++++++++++++- .../qml/controls-uit/HorizontalSpacer.qml | 19 ++ interface/resources/qml/hifi/Card.qml | 64 +++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml create mode 100644 interface/resources/qml/hifi/Card.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index dc060b70e1..8ac199b326 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -11,8 +11,13 @@ import Hifi 1.0 import QtQuick 2.4 import "controls" +import "controls-uit" as HifiControls import "styles" import "windows" +import "hifi" + +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 Window { id: root @@ -43,12 +48,52 @@ Window { // Explicitly center in order to avoid warnings at shutdown anchors.centerIn = parent; } - + + function goCard(card) { + addressLine.text = card.userStory.name; + toggleOrGo(); + } + AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + Column { + width: backgroundImage.width; + anchors { + left: backgroundImage.left; + leftMargin: backgroundImage.height + 2 * hifi.layout.spacing + bottom: backgroundImage.top; + bottomMargin: 2 * hifi.layout.spacing; + } + Text { + text: "Suggestions" + } + Row { + Card { + id: s0; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s1; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s2; + width: 200; + height: 200; + goFunction: goCard + } + } + } + Image { id: backgroundImage source: "../images/address-bar.svg" @@ -135,9 +180,124 @@ Window { } } + function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. + // TODO: make available to other .qml. + var request = new XMLHttpRequest(); + // QT bug: apparently doesn't handle onload. Workaround using readyState. + request.onreadystatechange = function () { + var READY_STATE_DONE = 4; + var HTTP_OK = 200; + if (request.readyState >= READY_STATE_DONE) { + var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, + response = !error && request.responseText, + contentType = !error && request.getResponseHeader('content-type'); + if (!error && contentType.indexOf('application/json') === 0) { + try { + response = JSON.parse(response); + } catch (e) { + error = e; + } + } + cb(error, response); + } + }; + request.open("GET", url, true); + request.send(); + } + // call iterator(element, icb) once for each element of array, and then cb(error) when icb(error) has been called by each iterator. + // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + function asyncEach(array, iterator, cb) { + var count = array.length; + function icb(error) { + if (!--count || error) { + count = -1; // don't cb multiple times (e.g., if error) + cb(error); + } + } + if (!count) { + return cb(); + } + array.forEach(function (element) { + iterator(element, icb); + }); + } + + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) + asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { + var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; + getRequest(url, function (error, json) { + var previews = !error && json.data.place.previews; + if (previews) { + if (!domainInfo.thumbnail) { // just grab tghe first one + domainInfo.thumbnail = previews.thumbnail; + } + if (!domainInfo.lobby) { + domainInfo.lobby = previews.lobby; + } + } + icb(error); + }); + }, cb); + } + + function getDomains(options, cb) { // cb(error, arrayOfData) + if (!options.page) { + options.page = 1; + } + // FIXME: really want places I'm allowed in, not just open ones + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + getRequest(url, function (error, json) { + if (!error && (json.status !== 'success')) { + error = new Error("Bad response: " + JSON.stringify(json)); + } + if (error) { + error.message += ' for ' + url; + return cb(error); + } + var domains = json.data.domains; + asyncEach(domains, addPictureToDomain, function (error) { + if (json.current_page < json.total_pages) { + options.page++; + return getDomains(options, function (error, others) { + cb(error, domains.concat(others)); + }); + } + cb(null, domains); + }); + }); + } + + function fillDestinations () { + function fill1(target, data) { + if (!data) { + return; + } + console.log('suggestion:', JSON.stringify(data)); + target.userStory = data; + if (data.lobby) { + console.log('target', target); + console.log('target.image', target.image); + console.log('url', data.lobby); + target.image.source = data.lobby; + } + target.placeText = data.name; + target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); + } + getDomains({minUsers: 0, maxUsers: 20}, function (e, d) { + var withLobby = !e && d.filter(function (d1) { return d1.lobby; }); + console.log(e, d.length, withLobby.length); + withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); + console.log(withLobby.map(function (d) { return d.online_users; })); + fill1(s0, withLobby[0]); + fill1(s1, withLobby[1]); + fill1(s2, withLobby[2]); + }); + } + onVisibleChanged: { if (visible) { addressLine.forceActiveFocus() + fillDestinations(); } else { addressLine.text = "" } diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..a9fac2bcf7 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,19 @@ +// +// HorizontalSpacer.qml +// +// Created by Howard Stearns on 12 July 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../styles-uit" + +Item { + HifiConstants { id: hifi } + height: 1 // Must be non-zero + width: hifi.dimensions.controlInterlineHeight +} diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml new file mode 100644 index 0000000000..342c469303 --- /dev/null +++ b/interface/resources/qml/hifi/Card.qml @@ -0,0 +1,64 @@ +// +// Card.qml +// qml/hifi +// +// Displays a clickable card representing a user story or destination. +// +// Created by Howard Stearns on 7/13/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.5 +import "../styles-uit" + +Rectangle { + property var goFunction: null; + property var userStory: null; + property alias image: lobby; + property alias placeText: place.text; + property alias usersText: users.text; + HifiConstants { id: hifi } + Image { + id: lobby; + width: parent.width; + height: parent.height; + fillMode: Image.PreserveAspectCrop; + // source gets filled in later + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + onStatusChanged: { + if (status == Image.Error) { + console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); + // FIXME: let's get our own + source = "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif" + } + } + } + RalewaySemiBold { + id: place; + color: hifi.colors.lightGrayText; + anchors { + leftMargin: 2 * hifi.layout.spacing; + topMargin: 2 * hifi.layout.spacing; + } + } + RalewayRegular { + id: users; + color: hifi.colors.lightGrayText; + anchors { + bottom: parent.bottom; + right: parent.right; + bottomMargin: 2 * hifi.layout.spacing; + rightMargin: 2 * hifi.layout.spacing; + } + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: goFunction(parent); + } +} From 1c2d13d1eea38b5d88630decb110250fc8c79b37 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 14 Jul 2016 15:38:22 -0700 Subject: [PATCH 02/34] Don't show where you are now. --- interface/resources/qml/AddressBarDialog.qml | 8 ++++---- interface/src/ui/AddressBarDialog.cpp | 4 ++++ interface/src/ui/AddressBarDialog.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8ac199b326..fe5c1d8030 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -283,11 +283,11 @@ Window { target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); } - getDomains({minUsers: 0, maxUsers: 20}, function (e, d) { - var withLobby = !e && d.filter(function (d1) { return d1.lobby; }); - console.log(e, d.length, withLobby.length); + getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { + var here = addressBarDialog.getHost(); // don't show where we are now. + var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); + console.log(error, domains.length, withLobby.length); withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - console.log(withLobby.map(function (d) { return d.online_users; })); fill1(s0, withLobby[0]); fill1(s1, withLobby[1]); fill1(s2, withLobby[2]); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 6fb437e312..7a32381db3 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,6 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } +QString AddressBarDialog::getHost() const { + return DependencyManager::get()->getHost(); +} + void AddressBarDialog::loadAddress(const QString& address) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index bbce52c67c..b5a21dd47a 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,6 +34,7 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); + Q_INVOKABLE QString getHost() const; Q_INVOKABLE void loadAddress(const QString& address); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); From ce99146733deb50a425aa200ba213e056112a4b8 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 10:27:52 -0700 Subject: [PATCH 03/34] filter by typing --- interface/resources/qml/AddressBarDialog.qml | 53 +++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index fe5c1d8030..bcb55ba568 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -16,9 +16,6 @@ import "styles" import "windows" import "hifi" -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - Window { id: root HifiConstants { id: hifi } @@ -48,12 +45,13 @@ Window { // Explicitly center in order to avoid warnings at shutdown anchors.centerIn = parent; } - + function goCard(card) { addressLine.text = card.userStory.name; toggleOrGo(); } - + property var suggestionChoices: null; + AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width @@ -63,8 +61,8 @@ Window { width: backgroundImage.width; anchors { left: backgroundImage.left; - leftMargin: backgroundImage.height + 2 * hifi.layout.spacing bottom: backgroundImage.top; + leftMargin: parent.height + 2 * hifi.layout.spacing; bottomMargin: 2 * hifi.layout.spacing; } Text { @@ -175,6 +173,7 @@ Window { } font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 helperText: "Go to: place, @user, /path, network address" + onTextChanged: filterChoicesByText() } } @@ -184,7 +183,7 @@ Window { // TODO: make available to other .qml. var request = new XMLHttpRequest(); // QT bug: apparently doesn't handle onload. Workaround using readyState. - request.onreadystatechange = function () { + request.onreadystatechange = function () { var READY_STATE_DONE = 4; var HTTP_OK = 200; if (request.readyState >= READY_STATE_DONE) { @@ -221,7 +220,7 @@ Window { iterator(element, icb); }); } - + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; @@ -266,34 +265,52 @@ Window { }); }); } - - function fillDestinations () { + + function filterChoicesByText() { function fill1(target, data) { if (!data) { + target.visible = false; return; } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; if (data.lobby) { - console.log('target', target); - console.log('target.image', target.image); - console.log('url', data.lobby); target.image.source = data.lobby; } target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); + target.visible = true; } + if (!suggestionChoices) { + return; + } + var words = addressLine.text.toUpperCase().split(/\s+/); + var filtered = !words.length ? suggestionChoices : suggestionChoices.filter(function (domain) { + var text = domain.names.concat(domain.tags).join(' '); + if (domain.description) { + text += domain.description; + } + text = text.toUpperCase(); + return words.every(function (word) { + return text.indexOf(word) >= 0; + }); + }); + fill1(s0, filtered[0]); + fill1(s1, filtered[1]); + fill1(s2, filtered[2]); + } + + function fillDestinations() { + suggestionChoices = null; getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { var here = addressBarDialog.getHost(); // don't show where we are now. var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); - console.log(error, domains.length, withLobby.length); withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - fill1(s0, withLobby[0]); - fill1(s1, withLobby[1]); - fill1(s2, withLobby[2]); + suggestionChoices = withLobby; + filterChoicesByText(); }); } - + onVisibleChanged: { if (visible) { addressLine.forceActiveFocus() From 8faa961c8dfdb3f8586c73545708250bd6a4bca2 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 11:41:00 -0700 Subject: [PATCH 04/34] all domains, not just the 'active' ones, which turns out to be defined as having at least one person in it. --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index bcb55ba568..b9b6e58913 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -244,7 +244,7 @@ Window { options.page = 1; } // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); From 99e229467bdaec842c1aa58cb0b8adac97f72c3d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 11:49:21 -0700 Subject: [PATCH 05/34] Don't show the word "suggesions" when there aren't any. --- interface/resources/qml/AddressBarDialog.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b9b6e58913..220aa794b9 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -66,6 +66,7 @@ Window { bottomMargin: 2 * hifi.layout.spacing; } Text { + id: suggestionsLabel; text: "Suggestions" } Row { @@ -298,6 +299,7 @@ Window { fill1(s0, filtered[0]); fill1(s1, filtered[1]); fill1(s2, filtered[2]); + suggestionsLabel.visible = filtered.length; } function fillDestinations() { From 6fb2cedfe04b2248406718adaa4fe856b21542df Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 13:42:31 -0700 Subject: [PATCH 06/34] card default image: settable, use to start, and reset when needed --- interface/resources/qml/AddressBarDialog.qml | 4 +--- interface/resources/qml/hifi/Card.qml | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 220aa794b9..4cb550cd53 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -275,9 +275,7 @@ Window { } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; - if (data.lobby) { - target.image.source = data.lobby; - } + target.image.source = data.lobby || ''; // should fail to load and thus use default target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 342c469303..8b2d1133da 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -21,11 +21,14 @@ Rectangle { property alias image: lobby; property alias placeText: place.text; property alias usersText: users.text; + // FIXME: let's get our own + property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; HifiConstants { id: hifi } Image { id: lobby; width: parent.width; height: parent.height; + source: defaultPicture; fillMode: Image.PreserveAspectCrop; // source gets filled in later anchors.verticalCenter: parent.verticalCenter; @@ -33,8 +36,7 @@ Rectangle { onStatusChanged: { if (status == Image.Error) { console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); - // FIXME: let's get our own - source = "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif" + source = defaultPicture; } } } From 87e54482dda406b1853ffb826dd3b9115b7d5d2e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:30:28 -0700 Subject: [PATCH 07/34] check pictures only for the domains that have someone in them, but use all domains when filtering (occupied or no, picture or no). Also, make darn sure we use default picture if no data. --- interface/resources/qml/AddressBarDialog.qml | 52 ++++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 4cb550cd53..d87cfe1f48 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -50,7 +50,8 @@ Window { addressLine.text = card.userStory.name; toggleOrGo(); } - property var suggestionChoices: null; + property var allDomains: []; + property var suggestionChoices: []; AddressBarDialog { id: addressBarDialog @@ -223,12 +224,14 @@ Window { } function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) + // This requests data for all the names at once, and just uses the first one to come back. + // We might change this to check one at a time, which would be less requests and more latency. asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; getRequest(url, function (error, json) { var previews = !error && json.data.place.previews; if (previews) { - if (!domainInfo.thumbnail) { // just grab tghe first one + if (!domainInfo.thumbnail) { // just grab the first one domainInfo.thumbnail = previews.thumbnail; } if (!domainInfo.lobby) { @@ -255,15 +258,13 @@ Window { return cb(error); } var domains = json.data.domains; - asyncEach(domains, addPictureToDomain, function (error) { - if (json.current_page < json.total_pages) { - options.page++; - return getDomains(options, function (error, others) { - cb(error, domains.concat(others)); - }); - } - cb(null, domains); - }); + if (json.current_page < json.total_pages) { + options.page++; + return getDomains(options, function (error, others) { + cb(error, domains.concat(others)); + }); + } + cb(null, domains); }); } @@ -275,16 +276,13 @@ Window { } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; - target.image.source = data.lobby || ''; // should fail to load and thus use default + target.image.source = data.lobby || target.defaultPicture; target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; } - if (!suggestionChoices) { - return; - } var words = addressLine.text.toUpperCase().split(/\s+/); - var filtered = !words.length ? suggestionChoices : suggestionChoices.filter(function (domain) { + var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { var text = domain.names.concat(domain.tags).join(' '); if (domain.description) { text += domain.description; @@ -301,13 +299,25 @@ Window { } function fillDestinations() { - suggestionChoices = null; + allDomains = suggestionChoices = []; getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { + if (error) { + console.log('domain query failed:', error); + return filterChoicesByText(); + } var here = addressBarDialog.getHost(); // don't show where we are now. - var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); - withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - suggestionChoices = withLobby; - filterChoicesByText(); + allDomains = domains.filter(function (domain) { return domain.name !== here; }); + allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); + // Whittle down suggestions to those that have at least one user, and try to get pictures. + suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; }); + asyncEach(domains, addPictureToDomain, function (error) { + if (error) { + console.log('place picture query failed:', error); + } + // Whittle down more by requiring a picture. + suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; }); + filterChoicesByText(); + }); }); } From 562446e7b86659712823df026512f27973f5d89a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:44:27 -0700 Subject: [PATCH 08/34] use AddressManager, rather than adding a trampoline to hostname in AddressBarDialog. --- interface/resources/qml/AddressBarDialog.qml | 2 +- interface/src/ui/AddressBarDialog.cpp | 4 ---- interface/src/ui/AddressBarDialog.h | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index d87cfe1f48..43a1c10c0f 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -305,7 +305,7 @@ Window { console.log('domain query failed:', error); return filterChoicesByText(); } - var here = addressBarDialog.getHost(); // don't show where we are now. + var here = AddressManager.hostname; // don't show where we are now. allDomains = domains.filter(function (domain) { return domain.name !== here; }); allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); // Whittle down suggestions to those that have at least one user, and try to get pictures. diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 7a32381db3..6fb437e312 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,10 +40,6 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } -QString AddressBarDialog::getHost() const { - return DependencyManager::get()->getHost(); -} - void AddressBarDialog::loadAddress(const QString& address) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index b5a21dd47a..bbce52c67c 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,7 +34,6 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); - Q_INVOKABLE QString getHost() const; Q_INVOKABLE void loadAddress(const QString& address); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); From cc367f7b49b635a10cc3a3cb7c6587364a52846f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:56:31 -0700 Subject: [PATCH 09/34] We don't need HorizontalSpacer.qml. --- interface/resources/qml/AddressBarDialog.qml | 4 +--- .../qml/controls-uit/HorizontalSpacer.qml | 19 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 43a1c10c0f..8764639972 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -11,7 +11,6 @@ import Hifi 1.0 import QtQuick 2.4 import "controls" -import "controls-uit" as HifiControls import "styles" import "windows" import "hifi" @@ -71,20 +70,19 @@ Window { text: "Suggestions" } Row { + spacing: hifi.layout.spacing; Card { id: s0; width: 200; height: 200; goFunction: goCard } - HifiControls.HorizontalSpacer { } Card { id: s1; width: 200; height: 200; goFunction: goCard } - HifiControls.HorizontalSpacer { } Card { id: s2; width: 200; diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml deleted file mode 100644 index a9fac2bcf7..0000000000 --- a/interface/resources/qml/controls-uit/HorizontalSpacer.qml +++ /dev/null @@ -1,19 +0,0 @@ -// -// HorizontalSpacer.qml -// -// Created by Howard Stearns on 12 July 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 - -import "../styles-uit" - -Item { - HifiConstants { id: hifi } - height: 1 // Must be non-zero - width: hifi.dimensions.controlInterlineHeight -} From 289617d4ce5a4add7e9e3921dc49a55247d84c9a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:14:29 -0700 Subject: [PATCH 10/34] styling --- interface/resources/qml/AddressBarDialog.qml | 31 ++++++++-------- interface/resources/qml/hifi/Card.qml | 37 ++++++++++++++++---- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8764639972..b739842d1c 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -51,6 +51,8 @@ Window { } property var allDomains: []; property var suggestionChoices: []; + property int cardWidth: 200; + property int cardHeight: 152; AddressBarDialog { id: addressBarDialog @@ -60,33 +62,29 @@ Window { Column { width: backgroundImage.width; anchors { - left: backgroundImage.left; bottom: backgroundImage.top; - leftMargin: parent.height + 2 * hifi.layout.spacing; bottomMargin: 2 * hifi.layout.spacing; - } - Text { - id: suggestionsLabel; - text: "Suggestions" + right: backgroundImage.right; + rightMargin: -104; // FIXME } Row { spacing: hifi.layout.spacing; Card { id: s0; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } Card { id: s1; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } Card { id: s2; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } } @@ -221,10 +219,14 @@ Window { }); } + function identity(x) { + return x; + } + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) // This requests data for all the names at once, and just uses the first one to come back. // We might change this to check one at a time, which would be less requests and more latency. - asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { + asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(identity), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; getRequest(url, function (error, json) { var previews = !error && json.data.place.previews; @@ -279,7 +281,7 @@ Window { target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; } - var words = addressLine.text.toUpperCase().split(/\s+/); + var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { var text = domain.names.concat(domain.tags).join(' '); if (domain.description) { @@ -293,7 +295,6 @@ Window { fill1(s0, filtered[0]); fill1(s1, filtered[1]); fill1(s2, filtered[2]); - suggestionsLabel.visible = filtered.length; } function fillDestinations() { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 8b2d1133da..3a2a52e3c0 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -13,6 +13,7 @@ import Hifi 1.0 import QtQuick 2.5 +import QtGraphicalEffects 1.0 import "../styles-uit" Rectangle { @@ -21,6 +22,8 @@ Rectangle { property alias image: lobby; property alias placeText: place.text; property alias usersText: users.text; + property int textPadding: 20; + property int textSize: 24; // FIXME: let's get our own property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; HifiConstants { id: hifi } @@ -40,22 +43,44 @@ Rectangle { } } } + DropShadow { + source: place; + anchors.fill: place; + horizontalOffset: 0; + radius: 2; + samples: 9; + color: hifi.colors.black; + verticalOffset: 1; + spread: 0; + } + DropShadow { + source: users; + anchors.fill: users; + horizontalOffset: 0; + radius: 2; + samples: 9; + color: hifi.colors.black; + verticalOffset: 1; + spread: 0; + } RalewaySemiBold { id: place; - color: hifi.colors.lightGrayText; + color: hifi.colors.white; + size: textSize; anchors { - leftMargin: 2 * hifi.layout.spacing; - topMargin: 2 * hifi.layout.spacing; + top: parent.top; + left: parent.left; + margins: textPadding; } } RalewayRegular { id: users; - color: hifi.colors.lightGrayText; + size: textSize; + color: hifi.colors.white; anchors { bottom: parent.bottom; right: parent.right; - bottomMargin: 2 * hifi.layout.spacing; - rightMargin: 2 * hifi.layout.spacing; + margins: textPadding; } } MouseArea { From f00114e53e3e0e8f93459670e950f6dac28b4ddb Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:46:20 -0700 Subject: [PATCH 11/34] Make it work with laser pointers (selection at least), and fix magic numbers. --- interface/resources/qml/hifi/Card.qml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 3a2a52e3c0..b4a709215f 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -43,25 +43,30 @@ Rectangle { } } } + property int dropHorizontalOffset: 0; + property int dropVerticalOffset: 1; + property int dropRadius: 2; + property int dropSamples: 9; + property int dropSpread: 0; DropShadow { source: place; anchors.fill: place; - horizontalOffset: 0; - radius: 2; - samples: 9; + horizontalOffset: dropHorizontalOffset; + verticalOffset: dropVerticalOffset; + radius: dropRadius; + samples: dropSamples; color: hifi.colors.black; - verticalOffset: 1; - spread: 0; + spread: dropSpread; } DropShadow { source: users; anchors.fill: users; - horizontalOffset: 0; - radius: 2; - samples: 9; + horizontalOffset: dropHorizontalOffset; + verticalOffset: dropVerticalOffset; + radius: dropRadius; + samples: dropSamples; color: hifi.colors.black; - verticalOffset: 1; - spread: 0; + spread: dropSpread; } RalewaySemiBold { id: place; @@ -87,5 +92,6 @@ Rectangle { anchors.fill: parent; acceptedButtons: Qt.LeftButton; onClicked: goFunction(parent); + hoverEnabled: true; } } From b106dee3e9cb876cbead876246f79fcbb825d947 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:52:13 -0700 Subject: [PATCH 12/34] Provide our own default image. --- interface/resources/images/default-domain.gif | Bin 0 -> 4714 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/images/default-domain.gif diff --git a/interface/resources/images/default-domain.gif b/interface/resources/images/default-domain.gif new file mode 100644 index 0000000000000000000000000000000000000000..1418a54cddf29bcbf4e3e32ec8b903a7d1ac88a4 GIT binary patch literal 4714 zcmb`?i8~XHqUc~9SM?n3P zv|Nx1PH?bSwAt%Ko8VMs7()HY`|I8@ci`zdZegZ=@s@$fS3M%HIKNkPf}6kjXyg-f z2cD(~gIfirs5`$m_KnqX4K;ZY2lY?UeHO0a9BS^Lbk!r;)Gty0c?979yNich03HAv zp!z@fe@_4)+ZgF*t}2;CD}CMTbG2X??GLSVsvgG$(%0c_(>1|X>bXNq0({^_^VB90 zVN?BcVJkiH(a-Nc@Uw!ozW7W=`apx@?a1(fs5$q#+ zL*>s#zq!6V%f_l-&B5C%zvzwCQ*B620ck5@%}i&Eu*&0pV(nZ{@+HfBD^lG8Jxkkr zvY&)s8Y;XQFKyjKSRO&&t#~}pRKGe_<4VuBZf;orNqn<9Indm=F+~X%xL`vjZq3os zRGtiyN!v@qC6)y?Els!ItLzjp@$#3%6Uzhuh4NiYG&@Eyw$tbM%7SZLNQg z54KmQhT7WLYyePk8p$Q=Ws)duElyhgX;T!PN1$6TWRB6{v4c#k=kWDHcAPc{|xoKG>A$IPc% z=`PQw*_tUWq}$*3T=?YRj#Jl@0RF zsP{(8!D7WmD{Id?iW9)2x=G;@^Vw|YRlsd_2cZN;HdBk~5lq(t7C+23moRolnx5#pSX+$SE1X32sFSH&;fepfP|0+uQ(I3a6W%PV7?uKu=c;0FzpRzjnuefV)Zd^i$g-j z5hz`l>V8y_z-c@IP^4I;{H+mq>IP7A6y*S-s5yU+iaA68+MHQjd90OL&SXAquT-x5 z;~%q}e(twZrtdy+)B#+3zG(9SzMcD!D9bsH(S8#(UvStos~;d5`Ye#E_y8-*g#d;| zON$hVvpgjBlnC!1+n36$%RqZ5wndKim)PPh4@A^#bp#NX%lF_e0H87f5X|*?7w)2) zsEaJ9(kYV#W5q-%BSkGcBD|ZPDoALxye!g_*SU)eH2ejld=8_)QWh&F5d>?(aFuS{ z_m=9ZmAYz`=y7Ap+3D}ep}YK+rJf@`ar~zQ1*B5_3QYe_`C$4wYC8Lh>c#B1dG)o=EGv2WN$U>fqyK0c}>}Pw%9_g5+v3mQuI; z&biy!**8A2z~v~~HrMci0HoYc?8-A8mi4w)fM%X!0Nk@;QlUo7t4MLJL(6Z{4w3n2 z%y&a!=Sbsh)@<9VllnK*Pd-j_RZhmv3;3!b*R+W*G0*vT=jG`szPsWv()nS&xluo@ z1p{2ph;oO04{$K)J zEBH=$^!H>N*OC6L=(M`a0xIhrIPF2RM-AUSku4D~t^pS;%}FrpZyI036JlrI_xI)N zYm;zs_pCJlt)eP#&6&@IY#yQ3HqVrX$Scn$sQa8`-_gUnmoY<4mV7)Vs{IbUl!T`c zuL-a8k{sJ@nv-uNPlCi)$7+DPTpvEpy_hC{Q&;4~FmklR;B@s~hRJO;?dZoQqpBq; z=Mo-i=(;|fNIa=n=IHKdxq0wx{l$rT(s>O}%hSJtzAG60r@5FJ+srqAe>~$JWa{*l z8~Lm95|v6fPK2pHojj=>R44Fa^D;VZQZ7D7uFz0hQ0n6JX}sS4=E+gNs2sNihAFpS zxhudJ=F)5?-J23;?OQG`oR}dIb>q6G{PxG$bB#0oc!0I1I{ECz_iV1q$X7A348%1$5NvqiVlzBpG^|VSk28G*|i9;yOND zbv@@+2*@TjAhPn$a;8w==uqW8;lZEOY>kL4T`$ zdIU{1PKM;|4*G3R&i=bH-#NSoA6J_8uYNlz^&xujiP==-ZC3iDC+ltdW`mhm4mYJh zM?vOI(`cjPFCQ-a4fG3{{P*kq>W2%|#W7$gPY+9zGUdcp;Ax1`2R6VDAx}BPdBS@1 zc6b7Kn9n922%g{6Xa5xg^69IIjRU7M^tqgozk}XM5P`%o{gb>#XK1Dh;)3XC{c}uX zCEiD0 zb^r$L8C*jH?=V##=mUR34D^=5Pdj*7=nKsCM4UPZ?aqvJ)(En9h}zqK|5RMy&jW); z9+5B2%>tPGNe`}Ck0YKiy@P1{nPOLS+{4}xeLfKR1&9WJ7b4xqLp*5QD!f^?f3rcYkiV+sJdgSLq)%IiGOpTaNeW1bdB~nvDBleziYO8k2Kc2K0YMQ-q!iAEL zjsQ53GnH>4Eg>TPP7OfXQIp$&duQBGH8wdfI10+g{VPk8PbS1BIc3l_a3c7VQ}`XG zxYj9BI@@%@&`iQk*5ZvzFV~$zP41eEXO3W}=bMOf-qN&0g**fgKdDV*C`I0$ z3D)}W(9eF~kTW(jLMb4`lj|fz+a#9DF&iAT9F`Po%S?~e8VNP?%L=pj)Xt2`RYJzc zYCW{zT4!E^VY2yp-wqILO@!lk`~U|`aJEEhZEO%#1YnJMBRrfrSps)1Or)7uk=`0< zZRe03KYbL>&g?~LyC(HWWOorV11$3NT~Uvbs2&rjFfGp%|4}s}Z*V7r+amv_Yu;3B z{@CS=(B6DQZNzbKj?s?!V~Yay`m}=yI8@1&<*4)9;KpXQjk!`9r<0D{0jg{{mTduK z8lEDUfQc&%a@boiB2n|x+C9L+M=PMq`+v*aTDskH9 zR|@Ana>SrKRD+W1cZ+?pOK$8#ihD~8M~Yi{O3#f!w9QM+ICD1M7MoL(^m|LM-Hmhr z@5R<=*G3CVIBTJX|qfdBYtz6Sml(7;4(L^TJ8Xs$PR8;2}%@+2mu-s0@ zU@KY=qrNaJY&a|0j|%S_e1^&tcQq4VmiLwij1o?EJ=EL zuTtN<$`p`14cGaS^Ldd!Vx1q5ms4e0Qn@Erp|u-+tW<4zsmg{@*=~31F1FefUsZcl zWo};eP`PG6u2PS`2%F>WiK)4U&&YdTZLk~Wqg;D0uuN3S{t6Q(N!Pw=P<1*UsT5p! zQAJ1OAslFFWVutRMAyEXjXT+llT+a~l*B`{>;C?zy|;kxE#rEkOZ^72zM;5&ol;MjtzQK+)X6q18#l0PTpE^;4OPVr3zP=j zY{ML&5i8p`W88>#X`DhfmKQhvqBNGyHvR+7Gw;uw;cUrhW?A);o9BLGs4 zEa{6eDa(a4gd}AYlLjcH^kTebZutRE(^-|KQmMM~kfyTSa%^+c!g$kBPE(Cab)8}J zv{Q4#F^-tq+}hMkUd6T^H|I-|J66%uhvfHgGVN!1Uo#o@gFHwt9hPbVUv2r;Ts#`m zB9+tfqq&grvxRloGEL8)HEd-#wl309%-q&JeCwK&(eLBdHQ}~DA=h^vw#~k2JNS9+ zsJU%?r0t*KH2{b*c&Q19pzxw7{A7wCgK~;RVGDuU&w|@UVC|xac5zg@B)J{LXqRTS z%YZs$!5x=j9rB0{MO23}xkHuFq0Z{i0Cj4CJ9S{4x`MI8KmBRYU5q)S>AC}yQWAs(A`f5P* zIxw98qc) zA_m4$13$h+F34u;r{`=T4@~s literal 0 HcmV?d00001 From f73c2b884dfa3ea4e3eb657d56ed19983f79bef0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 17:01:37 -0700 Subject: [PATCH 13/34] Remove now-superflous Column. --- interface/resources/qml/AddressBarDialog.qml | 40 ++++++++++---------- interface/resources/qml/hifi/Card.qml | 3 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b739842d1c..edc69d7c7d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -59,7 +59,7 @@ Window { implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height - Column { + Row { width: backgroundImage.width; anchors { bottom: backgroundImage.top; @@ -67,26 +67,24 @@ Window { right: backgroundImage.right; rightMargin: -104; // FIXME } - Row { - spacing: hifi.layout.spacing; - Card { - id: s0; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } - Card { - id: s1; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } - Card { - id: s2; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } + spacing: hifi.layout.spacing; + Card { + id: s0; + width: cardWidth; + height: cardHeight; + goFunction: goCard + } + Card { + id: s1; + width: cardWidth; + height: cardHeight; + goFunction: goCard + } + Card { + id: s2; + width: cardWidth; + height: cardHeight; + goFunction: goCard } } diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index b4a709215f..7758c5800a 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -24,8 +24,7 @@ Rectangle { property alias usersText: users.text; property int textPadding: 20; property int textSize: 24; - // FIXME: let's get our own - property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; + property string defaultPicture: "../../images/default-domain.gif"; HifiConstants { id: hifi } Image { id: lobby; From d446e1bcf54eee13320b071910e689b5be910993 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 15 Jul 2016 19:15:58 -0700 Subject: [PATCH 14/34] Phone home with suggestions activity usage. --- interface/resources/qml/AddressBarDialog.qml | 6 +++--- interface/src/ui/AddressBarDialog.cpp | 4 ++-- interface/src/ui/AddressBarDialog.h | 2 +- libraries/networking/src/AddressManager.cpp | 4 ++-- libraries/networking/src/AddressManager.h | 5 +++-- libraries/networking/src/UserActivityLogger.cpp | 3 +++ 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index edc69d7c7d..e8318ef1b8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -47,7 +47,7 @@ Window { function goCard(card) { addressLine.text = card.userStory.name; - toggleOrGo(); + toggleOrGo(true); } property var allDomains: []; property var suggestionChoices: []; @@ -327,9 +327,9 @@ Window { } } - function toggleOrGo() { + function toggleOrGo(fromSuggestions) { if (addressLine.text !== "") { - addressBarDialog.loadAddress(addressLine.text) + addressBarDialog.loadAddress(addressLine.text, fromSuggestions) } root.shown = false; } diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 6fb437e312..a4ef8a913f 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,10 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } -void AddressBarDialog::loadAddress(const QString& address) { +void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { - DependencyManager::get()->handleLookupString(address); + DependencyManager::get()->handleLookupString(address, fromSuggestions); } } diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index bbce52c67c..6c7620164b 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,7 +34,7 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); - Q_INVOKABLE void loadAddress(const QString& address); + Q_INVOKABLE void loadAddress(const QString& address, bool fromSuggestions = false); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); Q_INVOKABLE void loadForward(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 7ed3888be0..b3a022cc3a 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -221,7 +221,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { return false; } -void AddressManager::handleLookupString(const QString& lookupString) { +void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl QString sanitizedString = lookupString.trimmed(); @@ -236,7 +236,7 @@ void AddressManager::handleLookupString(const QString& lookupString) { lookupURL = QUrl(lookupString); } - handleUrl(lookupURL); + handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput); } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index a3aaee3ba2..c013da3a72 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -49,7 +49,8 @@ public: StartupFromSettings, DomainPathResponse, Internal, - AttemptedRefresh + AttemptedRefresh, + Suggestions }; bool isConnected(); @@ -77,7 +78,7 @@ public: std::function localSandboxNotRunningDoThat); public slots: - void handleLookupString(const QString& lookupString); + void handleLookupString(const QString& lookupString, bool fromSuggestions = false); // we currently expect this to be called from NodeList once handleLookupString has been called with a path bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index eba4d31167..75e15db2a4 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -178,6 +178,9 @@ void UserActivityLogger::wentTo(AddressManager::LookupTrigger lookupTrigger, QSt case AddressManager::StartupFromSettings: trigger = "StartupFromSettings"; break; + case AddressManager::Suggestions: + trigger = "Suggesions"; + break; default: return; } From cf025072e16f2fee538af06b7b10b6715ac463a8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 16 Jul 2016 09:50:56 -0700 Subject: [PATCH 15/34] Only consider "active" domains for all purposes (which on the back end means at least one person connected). --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index e8318ef1b8..f08ef5c608 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -246,7 +246,7 @@ Window { options.page = 1; } // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); From 5a01bf406e267d7a90fd43792f2761f0197f781d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Jul 2016 11:29:41 -0700 Subject: [PATCH 16/34] remove obsolete handControllerMouse.js to avoid confusion --- .../system/controllers/handControllerMouse.js | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 scripts/system/controllers/handControllerMouse.js diff --git a/scripts/system/controllers/handControllerMouse.js b/scripts/system/controllers/handControllerMouse.js deleted file mode 100644 index 921999f96a..0000000000 --- a/scripts/system/controllers/handControllerMouse.js +++ /dev/null @@ -1,131 +0,0 @@ -// -// handControllerMouse.js -// examples/controllers -// -// Created by Brad Hefta-Gaub on 2015/12/15 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var DEBUGGING = false; -var angularVelocityTrailingAverage = 0.0; // Global trailing average used to decide whether to move reticle at all -var lastX = 0; -var lastY = 0; - -Math.clamp=function(a,b,c) { - return Math.max(b,Math.min(c,a)); -} - -function length(posA, posB) { - var dx = posA.x - posB.x; - var dy = posA.y - posB.y; - var length = Math.sqrt((dx*dx) + (dy*dy)) - return length; -} - -function moveReticleAbsolute(x, y) { - var globalPos = Reticle.getPosition(); - globalPos.x = x; - globalPos.y = y; - Reticle.setPosition(globalPos); -} - -var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation"; -var mapping = Controller.newMapping(MAPPING_NAME); -if (Controller.Hardware.Hydra !== undefined) { - mapping.from(Controller.Hardware.Hydra.L3).peek().to(Controller.Actions.ReticleClick); - mapping.from(Controller.Hardware.Hydra.R4).peek().to(Controller.Actions.ReticleClick); -} -if (Controller.Hardware.Vive !== undefined) { - mapping.from(Controller.Hardware.Vive.LeftPrimaryThumb).peek().to(Controller.Actions.ReticleClick); - mapping.from(Controller.Hardware.Vive.RightPrimaryThumb).peek().to(Controller.Actions.ReticleClick); -} - -mapping.enable(); - -function debugPrint(message) { - if (DEBUGGING) { - print(message); - } -} - -var leftRightBias = 0.0; -var filteredRotatedLeft = Vec3.UNIT_NEG_Y; -var filteredRotatedRight = Vec3.UNIT_NEG_Y; -var lastAlpha = 0; - -Script.update.connect(function(deltaTime) { - - // avatar frame - var poseRight = Controller.getPoseValue(Controller.Standard.RightHand); - var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand); - - // NOTE: hack for now - var screenSize = Reticle.maximumPosition; - var screenSizeX = screenSize.x; - var screenSizeY = screenSize.y; - - // transform hand facing vectors from avatar frame into sensor frame. - var worldToSensorMatrix = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - var rotatedRight = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y))); - var rotatedLeft = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y))); - - lastRotatedRight = rotatedRight; - - // Decide which hand should be controlling the pointer - // by comparing which one is moving more, and by - // tending to stay with the one moving more. - if (deltaTime > 0.001) { - // leftRightBias is a running average of the difference in angular hand speed. - // a positive leftRightBias indicates the right hand is spinning faster then the left hand. - // a negative leftRightBias indicates the left hand is spnning faster. - var BIAS_ADJUST_PERIOD = 1.0; - var tau = Math.clamp(deltaTime / BIAS_ADJUST_PERIOD, 0, 1); - newLeftRightBias = Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity); - leftRightBias = (1 - tau) * leftRightBias + tau * newLeftRightBias; - } - - // add a bit of hysteresis to prevent control flopping back and forth - // between hands when they are both mostly stationary. - var alpha; - var HYSTERESIS_OFFSET = 0.25; - if (lastAlpha > 0.5) { - // prefer right hand over left - alpha = leftRightBias > -HYSTERESIS_OFFSET ? 1 : 0; - } else { - alpha = leftRightBias > HYSTERESIS_OFFSET ? 1 : 0; - } - lastAlpha = alpha; - - // Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers - var VELOCITY_FILTER_GAIN = 0.5; - filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); - filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0)); - var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, alpha); - - var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again... - var absoluteYaw = -rotated.x; // from -1 left to 1 right - - var x = Math.clamp(screenSizeX * (absoluteYaw + 0.5), 0, screenSizeX); - var y = Math.clamp(screenSizeX * absolutePitch, 0, screenSizeY); - - // don't move the reticle with the hand controllers unless the controllers are actually being moved - // take a time average of angular velocity, and don't move mouse at all if it's below threshold - - var AVERAGING_INTERVAL = 0.95; - var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.03; - var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - alpha) + Vec3.length(poseRight.angularVelocity) * alpha; - angularVelocityTrailingAverage = angularVelocityTrailingAverage * AVERAGING_INTERVAL + angularVelocityMagnitude * (1.0 - AVERAGING_INTERVAL); - - if ((angularVelocityTrailingAverage > MINIMUM_CONTROLLER_ANGULAR_VELOCITY) && ((x != lastX) || (y != lastY))) { - moveReticleAbsolute(x, y); - lastX = x; - lastY = y; - } -}); - -Script.scriptEnding.connect(function(){ - mapping.disable(); -}); From 959a2f9915e6ef06816135cf4d25b2b9d2b68c46 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Jul 2016 13:10:16 -0700 Subject: [PATCH 17/34] far-grabbed objects no longer fly away when the avatar body shifts Before this fix, a hand vector was being tracked that was the difference between the avatar's hand in avatar space with the avatar's hand in avatar space the previous frame. This hand vector was used to move the grabbed object position. However, when the body shifts, objects in the avatar's space will change rapidly, this would cause this hand vector to be incorrect and cause the object to shoot off in the distance. Now, we track this hand delta in sensor a.k.a. room space. This is immune to the shifts caused by body shifting. --- .../system/controllers/handControllerGrab.js | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 66c9e10795..50d5d644ac 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1592,13 +1592,16 @@ function MyController(hand) { this.clearEquipHaptics(); // controller pose is in avatar frame - var avatarControllerPose = - Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand); + var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var avatarControllerPose = Controller.getPoseValue(device); // transform it into world frame - var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation); - var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar); - var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); + var worldControllerPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + + // also transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var now = Date.now(); @@ -1609,7 +1612,7 @@ function MyController(hand) { this.currentObjectTime = now; this.currentCameraOrientation = Camera.orientation; - this.grabRadius = Vec3.distance(this.currentObjectPosition, controllerPosition); + this.grabRadius = Vec3.distance(this.currentObjectPosition, worldControllerPosition); this.grabRadialVelocity = 0.0; // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object @@ -1644,8 +1647,7 @@ function MyController(hand) { this.turnOffVisualizations(); - this.previousControllerPositionVSAvatar = controllerPositionVSAvatar; - this.previousControllerRotation = controllerRotation; + this.previousRoomControllerPosition = roomControllerPosition; }; this.distanceHolding = function (deltaTime, timestamp) { @@ -1658,13 +1660,17 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); // controller pose is in avatar frame - var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? - Controller.Standard.RightHand : Controller.Standard.LeftHand); + var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var avatarControllerPose = Controller.getPoseValue(device); // transform it into world frame - var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation); - var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar); - var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); + var worldControllerPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + var worldControllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); + + // also transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -1673,26 +1679,15 @@ function MyController(hand) { this.currentObjectTime = now; // the action was set up when this.distanceHolding was called. update the targets. - var radius = Vec3.distance(this.currentObjectPosition, controllerPosition) * + var radius = Vec3.distance(this.currentObjectPosition, worldControllerPosition) * this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; if (radius < 1.0) { radius = 1.0; } - // scale delta controller hand movement by radius. - var handMoved = Vec3.multiply(Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar), - radius); - - /// double delta controller rotation - // var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did - // var handChange = Quat.multiply(Quat.slerp(this.previousControllerRotation, - // controllerRotation, - // DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - // Quat.inverse(this.previousControllerRotation)); - - // update the currentObject position and rotation. + var handDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition); + var handMoved = Vec3.multiply(handDelta, radius); this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); - // this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); this.callEntityMethodOnGrabbed("continueDistantGrab"); @@ -1703,10 +1698,9 @@ function MyController(hand) { var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); // Update radialVelocity - var lastVelocity = Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar); - lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaObjectTime); - var newRadialVelocity = Vec3.dot(lastVelocity, - Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition))); + var lastVelocity = Vec3.multiply(handDelta, 1.0 / deltaObjectTime); + var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition)); + var newRadialVelocity = Vec3.dot(lastVelocity, delta); var VELOCITY_AVERAGING_TIME = 0.016; this.grabRadialVelocity = (deltaObjectTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity + @@ -1718,9 +1712,8 @@ function MyController(hand) { this.grabRadius * RADIAL_GRAB_AMPLIFIER); } - var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(controllerRotation)); - newTargetPosition = Vec3.sum(newTargetPosition, controllerPosition); - + var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); if (handControllerData.disableMoveWithHead !== true) { @@ -1776,8 +1769,7 @@ function MyController(hand) { print("continueDistanceHolding -- updateAction failed"); } - this.previousControllerPositionVSAvatar = controllerPositionVSAvatar; - this.previousControllerRotation = controllerRotation; + this.previousRoomControllerPosition = roomControllerPosition; }; this.setupHoldAction = function () { From 1a66574adb44542157af46d04167645ef6533bb0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 14:29:49 -0700 Subject: [PATCH 18/34] add protocol version signature to metaverse heartbeat --- domain-server/src/DomainServer.cpp | 4 +++- libraries/networking/src/udt/PacketHeaders.cpp | 14 +++++++++++--- libraries/networking/src/udt/PacketHeaders.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8b3f09d1f7..bcfdd42c34 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1083,9 +1083,11 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { // Setup the domain object to send to the data server QJsonObject domainObject; - // add the version + // add the versions static const QString VERSION_KEY = "version"; domainObject[VERSION_KEY] = BuildInfo::VERSION; + static const QString PROTOCOL_KEY = "protocol"; + domainObject[PROTOCOL_KEY] = protocolVersionsSignatureBase64(); // add networking if (!networkAddress.isEmpty()) { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index fca006ae87..8542e9f60e 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -99,8 +99,9 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion) { } #endif -QByteArray protocolVersionsSignature() { - static QByteArray protocolVersionSignature; +static QByteArray protocolVersionSignature; +static QString protocolVersionSignatureBase64; +static void ensureProtocolVersionsSignature() { static std::once_flag once; std::call_once(once, [&] { QByteArray buffer; @@ -114,8 +115,11 @@ QByteArray protocolVersionsSignature() { QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(buffer); protocolVersionSignature = hash.result(); + protocolVersionSignatureBase64 = protocolVersionSignature.toBase64(); }); - +} +QByteArray protocolVersionsSignature() { + ensureProtocolVersionsSignature(); #if (PR_BUILD || DEV_BUILD) if (sendWrongProtocolVersion) { return QByteArray("INCORRECTVERSION"); // only for debugging version checking @@ -124,3 +128,7 @@ QByteArray protocolVersionsSignature() { return protocolVersionSignature; } +QString protocolVersionsSignatureBase64() { + ensureProtocolVersionsSignature(); + return protocolVersionSignatureBase64; +} diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 85030135a1..0e10a8fd76 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -113,6 +113,7 @@ extern const QSet NON_SOURCED_PACKETS; PacketVersion versionForPacketType(PacketType packetType); QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols +QString protocolVersionsSignatureBase64(); #if (PR_BUILD || DEV_BUILD) void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debugging version negotiation From 98f76924c5867307e14ac0329ef626a7542c56b2 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 14:49:56 -0700 Subject: [PATCH 19/34] log hearbeat like we do updates --- domain-server/src/DomainServer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bcfdd42c34..88f4b94883 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1121,7 +1121,12 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact))); static const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; - DependencyManager::get()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())), + QString path = DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())); +#if DEV_BUILD || PR_BUILD + qDebug() << "Domain metadata sent to" << path; + qDebug() << "Domain metadata update:" << domainUpdateJSON; +#endif + DependencyManager::get()->sendRequest(path, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, JSONCallbackParameters(nullptr, QString(), this, "handleMetaverseHeartbeatError"), From 11b461a73033867d00763d4c60f7532adae0aba4 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 19 Jul 2016 01:46:29 +0200 Subject: [PATCH 20/34] Fixes duplicate target overlays for teleporter, make sure to delete overlay before creating one --- scripts/system/controllers/teleport.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index da0b4cb576..854e23315e 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -88,6 +88,7 @@ function Teleporter() { this.createTargetOverlay = function() { + _this.deleteTargetOverlay(); var targetOverlayProps = { url: TARGET_MODEL_URL, dimensions: TARGET_MODEL_DIMENSIONS, @@ -191,6 +192,9 @@ function Teleporter() { }; this.deleteTargetOverlay = function() { + if (this.targetOverlay === null) { + return; + } Overlays.deleteOverlay(this.targetOverlay); this.intersection = null; this.targetOverlay = null; From 17e64cf5e6e4cc46660590f2e50d5e09273fcf20 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 16:58:29 -0700 Subject: [PATCH 21/34] suggest only unrestricted domains, and also use server to sort --- interface/resources/qml/AddressBarDialog.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index f08ef5c608..b806bbbca2 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -245,8 +245,10 @@ Window { if (!options.page) { options.page = 1; } - // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + // FIXME: really want places I'm allowed in, not just open ones. + // FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME: add maturity + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&restriction=open&sort_by=users&sort_order=desc&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); @@ -304,7 +306,6 @@ Window { } var here = AddressManager.hostname; // don't show where we are now. allDomains = domains.filter(function (domain) { return domain.name !== here; }); - allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); // Whittle down suggestions to those that have at least one user, and try to get pictures. suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; }); asyncEach(domains, addPictureToDomain, function (error) { From 7fd40a5d10747425a3c1ca03f82862fb06f91c05 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Jul 2016 17:08:17 -0700 Subject: [PATCH 22/34] MacOS: fix crash when touchscreen device is nullptr. --- interface/src/Application.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fb48472b14..eb0fbc4e13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2746,7 +2746,7 @@ void Application::touchUpdateEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchUpdateEvent(event); } - if (_touchscreenDevice->isActive()) { + if (_touchscreenDevice && _touchscreenDevice->isActive()) { _touchscreenDevice->touchUpdateEvent(event); } } @@ -2767,7 +2767,7 @@ void Application::touchBeginEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchBeginEvent(event); } - if (_touchscreenDevice->isActive()) { + if (_touchscreenDevice && _touchscreenDevice->isActive()) { _touchscreenDevice->touchBeginEvent(event); } @@ -2787,7 +2787,7 @@ void Application::touchEndEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchEndEvent(event); } - if (_touchscreenDevice->isActive()) { + if (_touchscreenDevice && _touchscreenDevice->isActive()) { _touchscreenDevice->touchEndEvent(event); } @@ -2795,7 +2795,7 @@ void Application::touchEndEvent(QTouchEvent* event) { } void Application::touchGestureEvent(QGestureEvent* event) { - if (_touchscreenDevice->isActive()) { + if (_touchscreenDevice && _touchscreenDevice->isActive()) { _touchscreenDevice->touchGestureEvent(event); } } From ad7fa971aa601ad1427694160d48409d9a07c095 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 18 Jul 2016 17:09:10 -0700 Subject: [PATCH 23/34] copy scripts into build directory --- interface/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index cf5a2b60ad..414fafe705 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -58,6 +58,7 @@ set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") # qt5_create_translation_custom(${QM} ${INTERFACE_SRCS} ${QT_UI_FILES} ${TS}) if (APPLE) + # configure CMake to use a custom Info.plist set_target_properties(${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in) @@ -229,6 +230,13 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") + # copy script files beside the executable + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/scripts" + $/../Resources/scripts + ) + # call the fixup_interface macro to add required bundling commands for installation fixup_interface() @@ -263,6 +271,7 @@ else (APPLE) endif (APPLE) if (SCRIPTS_INSTALL_DIR) + # setup install of scripts beside interface executable install( DIRECTORY "${CMAKE_SOURCE_DIR}/scripts/" From 57955a2b5612d287ad33dac1b96cf33fd1b33fec Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 18:12:27 -0700 Subject: [PATCH 24/34] Expose protocol version to qml, and use it in query. --- interface/resources/qml/AddressBarDialog.qml | 19 +++++++++++++++---- libraries/networking/src/AddressManager.cpp | 5 +++++ libraries/networking/src/AddressManager.h | 1 + 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b806bbbca2..0c9e5c9dbc 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -51,6 +51,7 @@ Window { } property var allDomains: []; property var suggestionChoices: []; + property var domainsBaseUrl: null; property int cardWidth: 200; property int cardHeight: 152; @@ -245,10 +246,20 @@ Window { if (!options.page) { options.page = 1; } - // FIXME: really want places I'm allowed in, not just open ones. - // FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. - // FIXME: add maturity - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&restriction=open&sort_by=users&sort_order=desc&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + if (!domainsBaseUrl) { + var domainsOptions = [ + 'open', // published hours handle now + 'active', // has at least one person connected. FIXME: really want any place that is verified accessible. + // FIXME: really want places I'm allowed in, not just open ones. + 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME add maturity + 'protocol=' + AddressManager.protocolVersion(), + 'sort_by=users', + 'sort_order=desc', + ]; + domainsBaseUrl = "https://metaverse.highfidelity.com/api/v1/domains/all?" + domainsOptions.join('&'); + } + var url = domainsBaseUrl + "&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index b3a022cc3a..ae6aad3c4f 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -24,6 +24,7 @@ #include "NodeList.h" #include "NetworkLogging.h" #include "UserActivityLogger.h" +#include "udt/PacketHeaders.h" const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; @@ -37,6 +38,10 @@ AddressManager::AddressManager() : } +QString AddressManager::protocolVersion() { + return protocolVersionsSignatureBase64(); +} + bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index c013da3a72..2e9f177137 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -39,6 +39,7 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + Q_INVOKABLE QString protocolVersion(); using PositionGetter = std::function; using OrientationGetter = std::function; From 20824f038c67b63aedf38481c359a9bc668b7822 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 18 Jul 2016 21:59:44 -0700 Subject: [PATCH 25/34] include codec in audio stream packets so that each side can discard packets that don't match --- assignment-client/src/audio/AudioMixer.cpp | 17 ++++++++++--- .../src/audio/AudioMixerClientData.cpp | 2 +- .../src/audio/AudioMixerClientData.h | 3 +++ libraries/audio-client/src/AudioClient.cpp | 5 ++-- .../audio/src/AbstractAudioInterface.cpp | 18 ++++++++++--- libraries/audio/src/AbstractAudioInterface.h | 3 ++- libraries/audio/src/AudioInjector.cpp | 12 +++++++++ libraries/audio/src/InboundAudioStream.cpp | 25 ++++++++++++++++--- libraries/audio/src/InjectedAudioStream.cpp | 1 + libraries/networking/src/udt/BasePacket.cpp | 5 ++-- .../networking/src/udt/PacketHeaders.cpp | 7 ++++++ libraries/networking/src/udt/PacketHeaders.h | 5 ++++ 12 files changed, 84 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 40e22f855a..f4b80f55b4 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -768,28 +768,39 @@ void AudioMixer::broadcastMixes() { std::unique_ptr mixPacket; + const int MAX_CODEC_NAME = 30; // way over estimate + if (mixHasAudio) { - int mixPacketBytes = sizeof(quint16) + AudioConstants::NETWORK_FRAME_BYTES_STEREO; + int mixPacketBytes = sizeof(quint16) + MAX_CODEC_NAME+ AudioConstants::NETWORK_FRAME_BYTES_STEREO; mixPacket = NLPacket::create(PacketType::MixedAudio, mixPacketBytes); // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); mixPacket->writePrimitive(sequence); + // write the codec + QString codecInPacket = nodeData->getCodecName(); + mixPacket->writeString(codecInPacket); + QByteArray decodedBuffer(reinterpret_cast(_clampedSamples), AudioConstants::NETWORK_FRAME_BYTES_STEREO); QByteArray encodedBuffer; nodeData->encode(decodedBuffer, encodedBuffer); // pack mixed audio samples mixPacket->write(encodedBuffer.constData(), encodedBuffer.size()); - } else { - int silentPacketBytes = sizeof(quint16) + sizeof(quint16); + } + else { + int silentPacketBytes = sizeof(quint16) + sizeof(quint16) + MAX_CODEC_NAME; mixPacket = NLPacket::create(PacketType::SilentAudioFrame, silentPacketBytes); // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); mixPacket->writePrimitive(sequence); + // write the codec + QString codecInPacket = nodeData->getCodecName(); + mixPacket->writeString(codecInPacket); + // pack number of silent audio samples quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; mixPacket->writePrimitive(numSilentSamples); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 5c2ce8bf57..dc481e1c59 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -128,7 +128,6 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { isMicStream = true; } else if (packetType == PacketType::InjectAudio) { // this is injected audio - // grab the stream identifier for this injected audio message.seek(sizeof(quint16)); QUuid streamIdentifier = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -167,6 +166,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // check the overflow count before we parse data auto overflowBefore = matchingStream->getOverflowCount(); + auto parseResult = matchingStream->parseData(message); if (matchingStream->getOverflowCount() > overflowBefore) { diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index da2bf8997c..85bf3fa3a1 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -78,6 +78,9 @@ public: } } + QString getCodecName() { return _selectedCodecName; } + + signals: void injectorStreamFinished(const QUuid& streamIdentifier); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7cf8574529..ac42de903d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -834,7 +834,7 @@ void AudioClient::handleAudioInput() { encodedBuffer = decocedBuffer; } - emitAudioPacket(encodedBuffer.constData(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, audioTransform, packetType); + emitAudioPacket(encodedBuffer.constData(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, audioTransform, packetType, _selectedCodecName); _stats.sentPacket(); } } @@ -852,7 +852,7 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { } // FIXME check a flag to see if we should echo audio? - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, audioTransform, PacketType::MicrophoneAudioWithEcho); + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, audioTransform, PacketType::MicrophoneAudioWithEcho, _selectedCodecName); } void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { @@ -1015,7 +1015,6 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { // no reason to lock access to the vector of injectors. if (!_activeLocalAudioInjectors.contains(injector)) { qDebug() << "adding new injector"; - _activeLocalAudioInjectors.append(injector); } else { qDebug() << "injector exists in active list already"; diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index b347d57450..aa5c85283c 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -19,7 +19,8 @@ #include "AudioConstants.h" -void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, const Transform& transform, PacketType packetType) { +void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, + const Transform& transform, PacketType packetType, QString codecName) { static std::mutex _mutex; using Locker = std::unique_lock; auto nodeList = DependencyManager::get(); @@ -27,10 +28,19 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes if (audioMixer && audioMixer->getActiveSocket()) { Locker lock(_mutex); auto audioPacket = NLPacket::create(packetType); + + // FIXME - this is not a good way to determine stereoness with codecs.... quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0; // write sequence number - audioPacket->writePrimitive(sequenceNumber++); + auto sequence = sequenceNumber++; + audioPacket->writePrimitive(sequence); + + // write the codec - don't include this for injected audio + if (packetType != PacketType::InjectAudio) { + auto stringSize = audioPacket->writeString(codecName); + } + if (packetType == PacketType::SilentAudioFrame) { // pack num silent samples quint16 numSilentSamples = isStereo ? @@ -49,8 +59,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes if (audioPacket->getType() != PacketType::SilentAudioFrame) { // audio samples have already been packed (written to networkAudioSamples) - audioPacket->setPayloadSize(audioPacket->getPayloadSize() + bytes); - static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); + int leadingBytes = audioPacket->getPayloadSize(); + audioPacket->setPayloadSize(leadingBytes + bytes); memcpy(audioPacket->getPayload() + leadingBytes, audioData, bytes); } nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index ee52622d7e..223421a7ab 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -28,7 +28,8 @@ class AbstractAudioInterface : public QObject { public: AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {}; - static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, const Transform& transform, PacketType packetType); + static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, const Transform& transform, + PacketType packetType, QString codecName = QString("")); public slots: virtual bool outputLocalInjector(bool isStereo, AudioInjector* injector) = 0; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 9c49ce66d8..5064686565 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -218,6 +218,14 @@ const uchar MAX_INJECTOR_VOLUME = 0xFF; static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; +qint64 writeStringToStream(const QString& string, QDataStream& stream) { + QByteArray data = string.toUtf8(); + uint32_t length = data.length(); + stream << static_cast(length); + stream << data; + return length + sizeof(uint32_t); +} + int64_t AudioInjector::injectNextFrame() { if (stateHas(AudioInjectorState::NetworkInjectionFinished)) { qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; @@ -264,6 +272,10 @@ int64_t AudioInjector::injectNextFrame() { // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; + // pack some placeholder sequence number for now + //QString noCodecForInjectors(""); + //writeStringToStream(noCodecForInjectors, audioPacketStream); + // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index c9b9363b1b..0a207acba0 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -58,6 +58,7 @@ void InboundAudioStream::reset() { _isStarved = true; _hasStarted = false; resetStats(); + //cleanupCodec(); // FIXME??? } void InboundAudioStream::resetStats() { @@ -99,12 +100,17 @@ void InboundAudioStream::perSecondCallbackForUpdatingStats() { } int InboundAudioStream::parseData(ReceivedMessage& message) { - + PacketType packetType = message.getType(); + // parse sequence number and track it quint16 sequence; message.readPrimitive(&sequence); SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence, message.getSourceID()); + QString codecInPacket(""); + if (packetType != PacketType::InjectAudio) { + codecInPacket = message.readString(); + } packetReceivedUpdateTimingStats(); @@ -112,9 +118,10 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // parse the info after the seq number and before the audio data (the stream properties) int prePropertyPosition = message.getPosition(); - int propertyBytes = parseStreamProperties(message.getType(), message.readWithoutCopy(message.getBytesLeftToRead()), networkSamples); + auto afterHeader = message.readWithoutCopy(message.getBytesLeftToRead()); + int propertyBytes = parseStreamProperties(message.getType(), afterHeader, networkSamples); message.seek(prePropertyPosition + propertyBytes); - + // handle this packet based on its arrival status. switch (arrivalInfo._status) { case SequenceNumberStats::Early: { @@ -129,9 +136,19 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { case SequenceNumberStats::OnTime: { // Packet is on time; parse its data to the ringbuffer if (message.getType() == PacketType::SilentAudioFrame) { + // FIXME - do some codecs need to know about these silen frames? writeDroppableSilentSamples(networkSamples); } else { - parseAudioData(message.getType(), message.readWithoutCopy(message.getBytesLeftToRead())); + // note: PCM and no codec are identical + bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == ""; + bool packetPCM = codecInPacket == "pcm" || codecInPacket == ""; + if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) { + auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); + parseAudioData(message.getType(), afterProperties); + } else { + qDebug() << __FUNCTION__ << "codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; + writeDroppableSilentSamples(networkSamples); + } } break; } diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index 54e0f92bea..ccd581959f 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -33,6 +33,7 @@ const uchar MAX_INJECTOR_VOLUME = 255; int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { + // setup a data stream to read from this packet QDataStream packetStream(packetAfterSeqNum); diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 18552ca966..c6501943c7 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -154,8 +154,8 @@ qint64 BasePacket::writeString(const QString& string) { QByteArray data = string.toUtf8(); uint32_t length = data.length(); writePrimitive(length); - writeData(data.constData(), data.length()); - seek(pos() + length); + write(data.constData(), data.length()); + //seek(pos() + length); return length + sizeof(uint32_t); } @@ -176,7 +176,6 @@ bool BasePacket::reset() { } qint64 BasePacket::writeData(const char* data, qint64 maxSize) { - Q_ASSERT_X(maxSize <= bytesAvailableForWrite(), "BasePacket::writeData", "not enough space for write"); // make sure we have the space required to write this block diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index fca006ae87..eb8739dd49 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -72,6 +72,13 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::DomainServerAddedNode: return static_cast(DomainServerAddedNodeVersion::PermissionsGrid); + case PacketType::MixedAudio: + case PacketType::SilentAudioFrame: + case PacketType::InjectAudio: + case PacketType::MicrophoneAudioNoEcho: + case PacketType::MicrophoneAudioWithEcho: + return static_cast(AudioVersion::CodecNameInAudioPackets); + default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 85030135a1..e723ea38eb 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -213,4 +213,9 @@ enum class DomainListVersion : PacketVersion { PermissionsGrid }; +enum class AudioVersion : PacketVersion { + HasCompressedAudio = 17, + CodecNameInAudioPackets +}; + #endif // hifi_PacketHeaders_h From 2e63aba8c96b84b76a892a4787f5ea1152784d7f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 18 Jul 2016 22:29:05 -0700 Subject: [PATCH 26/34] when getting unexpected codec in the mixer, send a message to the client to select a different codec --- assignment-client/src/audio/AudioMixer.cpp | 10 +--------- assignment-client/src/audio/AudioMixerClientData.cpp | 12 ++++++++++++ assignment-client/src/audio/AudioMixerClientData.h | 3 +++ libraries/audio/src/InboundAudioStream.cpp | 6 ++++++ libraries/audio/src/InboundAudioStream.h | 4 ++++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index f4b80f55b4..489f9afa2d 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -525,7 +525,6 @@ void AudioMixer::handleNegotiateAudioFormat(QSharedPointer mess } } - auto clientData = dynamic_cast(sendingNode->getLinkedData()); // FIXME - why would we not have client data at this point?? @@ -539,14 +538,7 @@ void AudioMixer::handleNegotiateAudioFormat(QSharedPointer mess clientData->setupCodec(selectedCodec, selectedCodecName); qDebug() << "selectedCodecName:" << selectedCodecName; - - auto replyPacket = NLPacket::create(PacketType::SelectedAudioFormat); - - // write them to our packet - replyPacket->writeString(selectedCodecName); - - auto nodeList = DependencyManager::get(); - nodeList->sendPacket(std::move(replyPacket), *sendingNode); + clientData->sendSelectAudioFormat(sendingNode, selectedCodecName); } void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) { diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index dc481e1c59..f055fded33 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -113,6 +113,8 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); qDebug() << "creating new AvatarAudioStream... codec:" << _selectedCodecName; + connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::sendSelectAudioFormat); + auto emplaced = _audioStreams.emplace( QUuid(), std::unique_ptr { avatarAudioStream } @@ -344,6 +346,16 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { return result; } +void AudioMixerClientData::sendSelectAudioFormat(SharedNodePointer node, const QString& selectedCodecName) { + auto replyPacket = NLPacket::create(PacketType::SelectedAudioFormat); + + // write them to our packet + replyPacket->writeString(selectedCodecName); + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(replyPacket), *node); +} + + void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& codecName) { cleanupCodec(); // cleanup any previously allocated coders first _codec = codec; diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 85bf3fa3a1..f4f190bd47 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -84,6 +84,9 @@ public: signals: void injectorStreamFinished(const QUuid& streamIdentifier); +public slots: + void sendSelectAudioFormat(SharedNodePointer node, const QString& selectedCodecName); + private: QReadWriteLock _streamsLock; AudioStreamMap _audioStreams; // microphone stream from avatar is stored under key of null UUID diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 0a207acba0..6b6ce0ad07 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "InboundAudioStream.h" @@ -147,7 +148,12 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { parseAudioData(message.getType(), afterProperties); } else { qDebug() << __FUNCTION__ << "codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; + writeDroppableSilentSamples(networkSamples); + + // inform others of the mismatch + auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); + emit mismatchedAudioCodec(sendingNode, _selectedCodecName); } } break; diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 5da63f96c2..af79ff6164 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -12,6 +12,7 @@ #ifndef hifi_InboundAudioStream_h #define hifi_InboundAudioStream_h +#include #include #include #include @@ -180,6 +181,9 @@ public: void setupCodec(CodecPluginPointer codec, const QString& codecName, int numChannels); void cleanupCodec(); +signals: + void mismatchedAudioCodec(SharedNodePointer sendingNode, const QString& desiredCodec); + public slots: /// This function should be called every second for all the stats to function properly. If dynamic jitter buffers /// is enabled, those stats are used to calculate _desiredJitterBufferFrames. From c6ffd81c4bfcb2022d4eeaf61c02a1b1c5af10d1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 18 Jul 2016 22:42:38 -0700 Subject: [PATCH 27/34] some cleanup --- assignment-client/src/audio/AudioMixer.cpp | 10 ++++------ assignment-client/src/audio/AudioMixerClientData.cpp | 3 --- assignment-client/src/audio/AudioMixerClientData.h | 1 - libraries/audio/src/AudioConstants.h | 2 ++ libraries/audio/src/AudioInjector.cpp | 6 +++--- libraries/audio/src/InboundAudioStream.cpp | 7 ++----- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 489f9afa2d..8f752e70d0 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -760,10 +760,9 @@ void AudioMixer::broadcastMixes() { std::unique_ptr mixPacket; - const int MAX_CODEC_NAME = 30; // way over estimate - if (mixHasAudio) { - int mixPacketBytes = sizeof(quint16) + MAX_CODEC_NAME+ AudioConstants::NETWORK_FRAME_BYTES_STEREO; + int mixPacketBytes = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + + AudioConstants::NETWORK_FRAME_BYTES_STEREO; mixPacket = NLPacket::create(PacketType::MixedAudio, mixPacketBytes); // pack sequence number @@ -780,9 +779,8 @@ void AudioMixer::broadcastMixes() { // pack mixed audio samples mixPacket->write(encodedBuffer.constData(), encodedBuffer.size()); - } - else { - int silentPacketBytes = sizeof(quint16) + sizeof(quint16) + MAX_CODEC_NAME; + } else { + int silentPacketBytes = sizeof(quint16) + sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE; mixPacket = NLPacket::create(PacketType::SilentAudioFrame, silentPacketBytes); // pack sequence number diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index f055fded33..85491537a2 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -168,7 +168,6 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { // check the overflow count before we parse data auto overflowBefore = matchingStream->getOverflowCount(); - auto parseResult = matchingStream->parseData(message); if (matchingStream->getOverflowCount() > overflowBefore) { @@ -348,8 +347,6 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() { void AudioMixerClientData::sendSelectAudioFormat(SharedNodePointer node, const QString& selectedCodecName) { auto replyPacket = NLPacket::create(PacketType::SelectedAudioFormat); - - // write them to our packet replyPacket->writeString(selectedCodecName); auto nodeList = DependencyManager::get(); nodeList->sendPacket(std::move(replyPacket), *node); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index f4f190bd47..babfae3539 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -80,7 +80,6 @@ public: QString getCodecName() { return _selectedCodecName; } - signals: void injectorStreamFinished(const QUuid& streamIdentifier); diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index dbbe434915..9271323498 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -26,6 +26,8 @@ namespace AudioConstants { inline const char* getAudioFrameName() { return "com.highfidelity.recording.Audio"; } + const int MAX_CODEC_NAME_LENGTH = 30; + const int MAX_CODEC_NAME_LENGTH_ON_WIRE = MAX_CODEC_NAME_LENGTH + sizeof(uint32_t); const int NETWORK_FRAME_BYTES_STEREO = 1024; const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample); const int NETWORK_FRAME_BYTES_PER_CHANNEL = 512; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 5064686565..527be70a59 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -272,9 +272,9 @@ int64_t AudioInjector::injectNextFrame() { // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; - // pack some placeholder sequence number for now - //QString noCodecForInjectors(""); - //writeStringToStream(noCodecForInjectors, audioPacketStream); + // current injectors don't use codecs, so pack in the unknown codec name + QString noCodecForInjectors(""); + writeStringToStream(noCodecForInjectors, audioPacketStream); // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 6b6ce0ad07..1fb908c1d0 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -59,7 +59,7 @@ void InboundAudioStream::reset() { _isStarved = true; _hasStarted = false; resetStats(); - //cleanupCodec(); // FIXME??? + cleanupCodec(); } void InboundAudioStream::resetStats() { @@ -108,10 +108,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { message.readPrimitive(&sequence); SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence, message.getSourceID()); - QString codecInPacket(""); - if (packetType != PacketType::InjectAudio) { - codecInPacket = message.readString(); - } + QString codecInPacket = message.readString(); packetReceivedUpdateTimingStats(); From c484fec51d3e2137132427a484db41f107db491c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 18 Jul 2016 22:45:28 -0700 Subject: [PATCH 28/34] cleanup --- libraries/audio/src/InboundAudioStream.cpp | 9 +++------ libraries/networking/src/udt/BasePacket.cpp | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 1fb908c1d0..ff177e9a93 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -116,8 +116,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // parse the info after the seq number and before the audio data (the stream properties) int prePropertyPosition = message.getPosition(); - auto afterHeader = message.readWithoutCopy(message.getBytesLeftToRead()); - int propertyBytes = parseStreamProperties(message.getType(), afterHeader, networkSamples); + int propertyBytes = parseStreamProperties(message.getType(), message.readWithoutCopy(message.getBytesLeftToRead()), networkSamples); message.seek(prePropertyPosition + propertyBytes); // handle this packet based on its arrival status. @@ -134,7 +133,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { case SequenceNumberStats::OnTime: { // Packet is on time; parse its data to the ringbuffer if (message.getType() == PacketType::SilentAudioFrame) { - // FIXME - do some codecs need to know about these silen frames? + // FIXME - Some codecs need to know about these silent frames... and can produce better output writeDroppableSilentSamples(networkSamples); } else { // note: PCM and no codec are identical @@ -144,10 +143,8 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); } else { - qDebug() << __FUNCTION__ << "codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; - + qDebug() << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; writeDroppableSilentSamples(networkSamples); - // inform others of the mismatch auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); emit mismatchedAudioCodec(sendingNode, _selectedCodecName); diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index c6501943c7..8a4b98de87 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -155,7 +155,6 @@ qint64 BasePacket::writeString(const QString& string) { uint32_t length = data.length(); writePrimitive(length); write(data.constData(), data.length()); - //seek(pos() + length); return length + sizeof(uint32_t); } From 1ee5023f6df63d816e4b8ec29c8530a91b7318ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 19 Jul 2016 08:33:54 -0700 Subject: [PATCH 29/34] fix warnings --- libraries/audio/src/AbstractAudioInterface.cpp | 6 ++---- libraries/audio/src/InboundAudioStream.cpp | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index aa5c85283c..bf43c35cb9 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -36,10 +36,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes auto sequence = sequenceNumber++; audioPacket->writePrimitive(sequence); - // write the codec - don't include this for injected audio - if (packetType != PacketType::InjectAudio) { - auto stringSize = audioPacket->writeString(codecName); - } + // write the codec + audioPacket->writeString(codecName); if (packetType == PacketType::SilentAudioFrame) { // pack num silent samples diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index ff177e9a93..d781a1991b 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -101,8 +101,6 @@ void InboundAudioStream::perSecondCallbackForUpdatingStats() { } int InboundAudioStream::parseData(ReceivedMessage& message) { - PacketType packetType = message.getType(); - // parse sequence number and track it quint16 sequence; message.readPrimitive(&sequence); From 212175bdaabf4db973a978f3ebba487a7987d4e1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 19 Jul 2016 10:23:00 -0700 Subject: [PATCH 30/34] bug fix for far-grab pulling the object in the wrong direction --- scripts/system/controllers/handControllerGrab.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 50d5d644ac..832346a5fd 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1685,8 +1685,9 @@ function MyController(hand) { radius = 1.0; } - var handDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition); - var handMoved = Vec3.multiply(handDelta, radius); + var roomHandDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition); + var worldHandDelta = Mat4.transformVector(MyAvatar.getSensorToWorldMatrix(), roomHandDelta); + var handMoved = Vec3.multiply(worldHandDelta, radius); this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); this.callEntityMethodOnGrabbed("continueDistantGrab"); @@ -1698,7 +1699,7 @@ function MyController(hand) { var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); // Update radialVelocity - var lastVelocity = Vec3.multiply(handDelta, 1.0 / deltaObjectTime); + var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition)); var newRadialVelocity = Vec3.dot(lastVelocity, delta); From f28f3d7fcf9adeb8ee841754b63e48b4f41b55ba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 19 Jul 2016 12:02:37 -0700 Subject: [PATCH 31/34] force removal of an old solo node when added new one --- libraries/networking/src/LimitedNodeList.cpp | 30 ++++++++++++++++++-- libraries/networking/src/LimitedNodeList.h | 3 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e2d6b277a7..d83046bc1b 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -34,7 +34,7 @@ #include "NetworkLogging.h" #include "udt/Packet.h" -const char SOLO_NODE_TYPES[2] = { +const std::set SOLO_NODE_TYPES = { NodeType::AvatarMixer, NodeType::AudioMixer }; @@ -534,7 +534,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t if (it != _nodeHash.end()) { SharedNodePointer& matchingNode = it->second; - + matchingNode->setPublicSocket(publicSocket); matchingNode->setLocalSocket(localSocket); matchingNode->setPermissions(permissions); @@ -551,7 +551,33 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t SharedNodePointer newNodePointer(newNode, &QObject::deleteLater); + // if this is a solo node type, we assume that the DS has replaced its assignment and we should kill the previous node + if (SOLO_NODE_TYPES.count(newNode->getType())) { + // while we still have the read lock, see if there is a previous solo node we'll need to remove + auto previousSoloIt = std::find_if(_nodeHash.cbegin(), _nodeHash.cend(), [newNode](const UUIDNodePair& nodePair){ + return nodePair.second->getType() == newNode->getType(); + }); + + if (previousSoloIt != _nodeHash.cend()) { + // we have a previous solo node, switch to a write lock so we can remove it + readLocker.unlock(); + + QWriteLocker writeLocker(&_nodeMutex); + + auto oldSoloNode = previousSoloIt->second; + + _nodeHash.unsafe_erase(previousSoloIt); + handleNodeKill(oldSoloNode); + + // convert the current lock back to a read lock for insertion of new node + writeLocker.unlock(); + readLocker.relock(); + } + } + + // insert the new node and release our read lock _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); + readLocker.unlock(); qCDebug(networking) << "Added" << *newNode; diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 03e82f053f..d599fbcc37 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifndef _WIN32 @@ -46,7 +47,7 @@ const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000; -extern const char SOLO_NODE_TYPES[2]; +extern const std::set SOLO_NODE_TYPES; const char DEFAULT_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost"; From 2adff24a058ab596bae5b427d55b03e72caaf99e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 19 Jul 2016 21:34:42 +0200 Subject: [PATCH 32/34] silly workaround- workaround --- scripts/system/controllers/teleport.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 854e23315e..3d40bfb9eb 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -88,11 +88,13 @@ function Teleporter() { this.createTargetOverlay = function() { - _this.deleteTargetOverlay(); + if (_this.targetOverlay !== null) { + return; + } var targetOverlayProps = { url: TARGET_MODEL_URL, dimensions: TARGET_MODEL_DIMENSIONS, - visible: true, + visible: true }; _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); @@ -620,4 +622,4 @@ function cleanup() { if (teleporter.updateConnected !== null) { Script.update.disconnect(teleporter.update); } -} \ No newline at end of file +} From 4a6132874f57b8ff463ee9117cd48ad9a6a15904 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 19 Jul 2016 13:43:28 -0700 Subject: [PATCH 33/34] encodeURIComponent of the protocol signature --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 0c9e5c9dbc..792410c59d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -253,7 +253,7 @@ Window { // FIXME: really want places I'm allowed in, not just open ones. 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. // FIXME add maturity - 'protocol=' + AddressManager.protocolVersion(), + 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), 'sort_by=users', 'sort_order=desc', ]; From 1c4742e7101d0ce10de82eb9ca2aed846930bd4d Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 20 Jul 2016 11:02:27 -0700 Subject: [PATCH 34/34] Fixing comments from review --- libraries/render-utils/src/DeferredFrameTransform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/DeferredFrameTransform.h b/libraries/render-utils/src/DeferredFrameTransform.h index 1abd912f15..df47396a38 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.h +++ b/libraries/render-utils/src/DeferredFrameTransform.h @@ -36,12 +36,12 @@ protected: // It s changing every frame class FrameTransform { public: - // Pixel info is { viemport width height and stereo on off} + // Pixel info is { viewport width height} glm::vec4 pixelInfo; glm::vec4 invpixelInfo; // Depth info is { n.f, f - n, -f} glm::vec4 depthInfo; - // Stereo info + // Stereo info is { isStereoFrame, halfWidth } glm::vec4 stereoInfo{ 0.0 }; // Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space glm::mat4 projection[2];