mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-08 21:47:30 +02:00
Merge pull request #10044 from howard-stearns/pal-backend-integration
Pal backend integration
This commit is contained in:
commit
0edbf1fbd9
9 changed files with 210 additions and 143 deletions
|
@ -162,17 +162,8 @@ Rectangle {
|
||||||
}
|
}
|
||||||
HifiControlsUit.TabletComboBox {
|
HifiControlsUit.TabletComboBox {
|
||||||
function determineAvailabilityIndex() {
|
function determineAvailabilityIndex() {
|
||||||
var globalServicesAvailability = GlobalServices.findableBy;
|
return ['all', 'connections', 'friends', 'none'].indexOf(GlobalServices.findableBy)
|
||||||
if (globalServicesAvailability === "all") {
|
}
|
||||||
return 0;
|
|
||||||
} else if (globalServicesAvailability === "friends") {
|
|
||||||
return 1;
|
|
||||||
} else if (globalServicesAvailability === "none") {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id: availabilityComboBox;
|
id: availabilityComboBox;
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: parent.top;
|
anchors.top: parent.top;
|
||||||
|
@ -184,6 +175,7 @@ Rectangle {
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
id: availabilityComboBoxListItems
|
id: availabilityComboBoxListItems
|
||||||
ListElement { text: "Everyone"; value: "all"; }
|
ListElement { text: "Everyone"; value: "all"; }
|
||||||
|
ListElement { text: "All Connections"; value: "connections"; }
|
||||||
ListElement { text: "Friends Only"; value: "friends"; }
|
ListElement { text: "Friends Only"; value: "friends"; }
|
||||||
ListElement { text: "Appear Offline"; value: "none" }
|
ListElement { text: "Appear Offline"; value: "none" }
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,10 @@ void DiscoverabilityManager::updateLocation() {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
auto addressManager = DependencyManager::get<AddressManager>();
|
auto addressManager = DependencyManager::get<AddressManager>();
|
||||||
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||||
|
bool discoverable = (_mode.get() != Discoverability::None);
|
||||||
|
|
||||||
|
|
||||||
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
|
if (accountManager->isLoggedIn()) {
|
||||||
// construct a QJsonObject given the user's current address information
|
// construct a QJsonObject given the user's current address information
|
||||||
QJsonObject rootObject;
|
QJsonObject rootObject;
|
||||||
|
|
||||||
|
@ -50,34 +51,36 @@ void DiscoverabilityManager::updateLocation() {
|
||||||
|
|
||||||
QString pathString = addressManager->currentPath();
|
QString pathString = addressManager->currentPath();
|
||||||
|
|
||||||
const QString PATH_KEY_IN_LOCATION = "path";
|
|
||||||
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
|
|
||||||
|
|
||||||
const QString CONNECTED_KEY_IN_LOCATION = "connected";
|
const QString CONNECTED_KEY_IN_LOCATION = "connected";
|
||||||
locationObject.insert(CONNECTED_KEY_IN_LOCATION, domainHandler.isConnected());
|
locationObject.insert(CONNECTED_KEY_IN_LOCATION, discoverable && domainHandler.isConnected());
|
||||||
|
|
||||||
if (!addressManager->getRootPlaceID().isNull()) {
|
if (discoverable) { // Don't consider changes to these as update-worthy if we're not discoverable.
|
||||||
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
|
const QString PATH_KEY_IN_LOCATION = "path";
|
||||||
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
|
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
|
||||||
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
|
|
||||||
|
if (!addressManager->getRootPlaceID().isNull()) {
|
||||||
|
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
|
||||||
|
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
|
||||||
|
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!domainHandler.getUUID().isNull()) {
|
||||||
|
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
|
||||||
|
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
|
||||||
|
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case the place/domain isn't in the database, we send the network address and port
|
||||||
|
auto& domainSockAddr = domainHandler.getSockAddr();
|
||||||
|
const QString NETWORK_ADDRESS_KEY_IN_LOCATION = "network_address";
|
||||||
|
locationObject.insert(NETWORK_ADDRESS_KEY_IN_LOCATION, domainSockAddr.getAddress().toString());
|
||||||
|
|
||||||
|
const QString NETWORK_ADDRESS_PORT_IN_LOCATION = "network_port";
|
||||||
|
locationObject.insert(NETWORK_ADDRESS_PORT_IN_LOCATION, domainSockAddr.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!domainHandler.getUUID().isNull()) {
|
const QString AVAILABILITY_KEY_IN_LOCATION = "availability";
|
||||||
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
|
locationObject.insert(AVAILABILITY_KEY_IN_LOCATION, findableByString(static_cast<Discoverability::Mode>(_mode.get())));
|
||||||
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
|
|
||||||
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case the place/domain isn't in the database, we send the network address and port
|
|
||||||
auto& domainSockAddr = domainHandler.getSockAddr();
|
|
||||||
const QString NETWORK_ADRESS_KEY_IN_LOCATION = "network_address";
|
|
||||||
locationObject.insert(NETWORK_ADRESS_KEY_IN_LOCATION, domainSockAddr.getAddress().toString());
|
|
||||||
|
|
||||||
const QString NETWORK_ADDRESS_PORT_IN_LOCATION = "network_port";
|
|
||||||
locationObject.insert(NETWORK_ADDRESS_PORT_IN_LOCATION, domainSockAddr.getPort());
|
|
||||||
|
|
||||||
const QString FRIENDS_ONLY_KEY_IN_LOCATION = "friends_only";
|
|
||||||
locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends));
|
|
||||||
|
|
||||||
JSONCallbackParameters callbackParameters;
|
JSONCallbackParameters callbackParameters;
|
||||||
callbackParameters.jsonCallbackReceiver = this;
|
callbackParameters.jsonCallbackReceiver = this;
|
||||||
|
@ -139,19 +142,29 @@ void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discov
|
||||||
|
|
||||||
// update the setting to the new value
|
// update the setting to the new value
|
||||||
_mode.set(static_cast<int>(discoverabilityMode));
|
_mode.set(static_cast<int>(discoverabilityMode));
|
||||||
|
updateLocation(); // update right away
|
||||||
if (static_cast<int>(_mode.get()) == Discoverability::None) {
|
|
||||||
// if we just got set to no discoverability, make sure that we delete our location in DB
|
|
||||||
removeLocation();
|
|
||||||
} else {
|
|
||||||
// we have a discoverability mode that says we should send a location, do that right away
|
|
||||||
updateLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit discoverabilityModeChanged(discoverabilityMode);
|
emit discoverabilityModeChanged(discoverabilityMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString DiscoverabilityManager::findableByString(Discoverability::Mode discoverabilityMode) {
|
||||||
|
if (discoverabilityMode == Discoverability::None) {
|
||||||
|
return "none";
|
||||||
|
} else if (discoverabilityMode == Discoverability::Friends) {
|
||||||
|
return "friends";
|
||||||
|
} else if (discoverabilityMode == Discoverability::Connections) {
|
||||||
|
return "connections";
|
||||||
|
} else if (discoverabilityMode == Discoverability::All) {
|
||||||
|
return "all";
|
||||||
|
} else {
|
||||||
|
qDebug() << "GlobalServices findableByString called with an unrecognized value.";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DiscoverabilityManager::setVisibility() {
|
void DiscoverabilityManager::setVisibility() {
|
||||||
Menu* menu = Menu::getInstance();
|
Menu* menu = Menu::getInstance();
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace Discoverability {
|
||||||
enum Mode {
|
enum Mode {
|
||||||
None,
|
None,
|
||||||
Friends,
|
Friends,
|
||||||
|
Connections,
|
||||||
All
|
All
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -42,6 +43,9 @@ public slots:
|
||||||
signals:
|
signals:
|
||||||
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);
|
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QString findableByString(Discoverability::Mode discoverabilityMode);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleHeartbeatResponse(QNetworkReply& requestReply);
|
void handleHeartbeatResponse(QNetworkReply& requestReply);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "HFWebEngineRequestInterceptor.h"
|
#include "HFWebEngineRequestInterceptor.h"
|
||||||
|
#include "NetworkingConstants.h"
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
@ -20,8 +21,11 @@ bool isAuthableHighFidelityURL(const QUrl& url) {
|
||||||
"highfidelity.com", "highfidelity.io",
|
"highfidelity.com", "highfidelity.io",
|
||||||
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
|
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
|
||||||
};
|
};
|
||||||
|
const auto& scheme = url.scheme();
|
||||||
|
const auto& host = url.host();
|
||||||
|
|
||||||
return url.scheme() == "https" && HF_HOSTS.contains(url.host());
|
return (scheme == "https" && HF_HOSTS.contains(host)) ||
|
||||||
|
((scheme == NetworkingConstants::METAVERSE_SERVER_URL.scheme()) && (host == NetworkingConstants::METAVERSE_SERVER_URL.host()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
||||||
|
|
|
@ -53,33 +53,19 @@ void GlobalServicesScriptingInterface::loggedOut() {
|
||||||
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
|
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString GlobalServicesScriptingInterface::findableByString(Discoverability::Mode discoverabilityMode) const {
|
|
||||||
if (discoverabilityMode == Discoverability::None) {
|
|
||||||
return "none";
|
|
||||||
} else if (discoverabilityMode == Discoverability::Friends) {
|
|
||||||
return "friends";
|
|
||||||
} else if (discoverabilityMode == Discoverability::All) {
|
|
||||||
return "all";
|
|
||||||
} else {
|
|
||||||
qDebug() << "GlobalServices findableByString called with an unrecognized value.";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString GlobalServicesScriptingInterface::getFindableBy() const {
|
QString GlobalServicesScriptingInterface::getFindableBy() const {
|
||||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||||
return findableByString(discoverabilityManager->getDiscoverabilityMode());
|
return DiscoverabilityManager::findableByString(discoverabilityManager->getDiscoverabilityMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
|
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
|
||||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||||
|
|
||||||
if (discoverabilityMode.toLower() == "none") {
|
if (discoverabilityMode.toLower() == "none") {
|
||||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
|
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
|
||||||
} else if (discoverabilityMode.toLower() == "friends") {
|
} else if (discoverabilityMode.toLower() == "friends") {
|
||||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
|
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
|
||||||
|
} else if (discoverabilityMode.toLower() == "connections") {
|
||||||
|
discoverabilityManager->setDiscoverabilityMode(Discoverability::Connections);
|
||||||
} else if (discoverabilityMode.toLower() == "all") {
|
} else if (discoverabilityMode.toLower() == "all") {
|
||||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
|
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,7 +74,7 @@ void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabil
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
|
void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
|
||||||
emit findableByChanged(findableByString(discoverabilityMode));
|
emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadInfoResult::DownloadInfoResult() :
|
DownloadInfoResult::DownloadInfoResult() :
|
||||||
|
|
|
@ -66,8 +66,6 @@ private:
|
||||||
GlobalServicesScriptingInterface();
|
GlobalServicesScriptingInterface();
|
||||||
~GlobalServicesScriptingInterface();
|
~GlobalServicesScriptingInterface();
|
||||||
|
|
||||||
QString findableByString(Discoverability::Mode discoverabilityMode) const;
|
|
||||||
|
|
||||||
bool _downloading;
|
bool _downloading;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,60 @@ function debug() {
|
||||||
print.apply(null, [].concat.apply([label, stateString, JSON.stringify(waitingList), connecting], [].map.call(arguments, JSON.stringify)));
|
print.apply(null, [].concat.apply([label, stateString, JSON.stringify(waitingList), connecting], [].map.call(arguments, JSON.stringify)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanId(guidWithCurlyBraces) {
|
||||||
|
return guidWithCurlyBraces.slice(1, -1);
|
||||||
|
}
|
||||||
|
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
|
||||||
|
var httpRequest = new XMLHttpRequest(), key;
|
||||||
|
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
||||||
|
httpRequest.onreadystatechange = function () {
|
||||||
|
var READY_STATE_DONE = 4;
|
||||||
|
var HTTP_OK = 200;
|
||||||
|
if (httpRequest.readyState >= READY_STATE_DONE) {
|
||||||
|
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
|
||||||
|
response = !error && httpRequest.responseText,
|
||||||
|
contentType = !error && httpRequest.getResponseHeader('content-type');
|
||||||
|
debug('FIXME REMOVE: server response', options, error, response, contentType);
|
||||||
|
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
|
||||||
|
try {
|
||||||
|
response = JSON.parse(response);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(error, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
options = {uri: options};
|
||||||
|
}
|
||||||
|
if (options.url) {
|
||||||
|
options.uri = options.url;
|
||||||
|
}
|
||||||
|
if (!options.method) {
|
||||||
|
options.method = 'GET';
|
||||||
|
}
|
||||||
|
if (options.body && (options.method === 'GET')) { // add query parameters
|
||||||
|
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
|
||||||
|
for (key in options.body) {
|
||||||
|
params.push(key + '=' + options.body[key]);
|
||||||
|
}
|
||||||
|
options.uri += appender + params.join('&');
|
||||||
|
delete options.body;
|
||||||
|
}
|
||||||
|
if (options.json) {
|
||||||
|
options.headers = options.headers || {};
|
||||||
|
options.headers["Content-type"] = "application/json";
|
||||||
|
options.body = JSON.stringify(options.body);
|
||||||
|
}
|
||||||
|
debug("FIXME REMOVE: final options to send", options);
|
||||||
|
for (key in options.headers || {}) {
|
||||||
|
httpRequest.setRequestHeader(key, options.headers[key]);
|
||||||
|
}
|
||||||
|
httpRequest.open(options.method, options.uri, true);
|
||||||
|
httpRequest.send(options.body);
|
||||||
|
}
|
||||||
|
|
||||||
function handToString(hand) {
|
function handToString(hand) {
|
||||||
if (hand === Controller.Standard.RightHand) {
|
if (hand === Controller.Standard.RightHand) {
|
||||||
return "RightHand";
|
return "RightHand";
|
||||||
|
@ -215,6 +269,7 @@ function deleteMakeConnectionParticleEffect() {
|
||||||
function stopHandshakeSound() {
|
function stopHandshakeSound() {
|
||||||
if (handshakeInjector) {
|
if (handshakeInjector) {
|
||||||
handshakeInjector.stop();
|
handshakeInjector.stop();
|
||||||
|
handshakeInjector = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,9 +448,7 @@ function endHandshake() {
|
||||||
stopWaiting();
|
stopWaiting();
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
stopMakingConnection();
|
stopMakingConnection();
|
||||||
if (handshakeInjector) {
|
stopHandshakeSound();
|
||||||
handshakeInjector.stop();
|
|
||||||
}
|
|
||||||
// send done to let connection know you are not making connections now
|
// send done to let connection know you are not making connections now
|
||||||
messageSend({
|
messageSend({
|
||||||
key: "done"
|
key: "done"
|
||||||
|
@ -405,6 +458,8 @@ function endHandshake() {
|
||||||
debug("removing animation");
|
debug("removing animation");
|
||||||
MyAvatar.removeAnimationStateHandler(animHandlerId);
|
MyAvatar.removeAnimationStateHandler(animHandlerId);
|
||||||
}
|
}
|
||||||
|
// No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us in a weird state.
|
||||||
|
request({uri: requestUrl, method: 'DELETE'}, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTriggers(value, fromKeyboard, hand) {
|
function updateTriggers(value, fromKeyboard, hand) {
|
||||||
|
@ -470,6 +525,64 @@ function lookForWaitingAvatar() {
|
||||||
}, WAITING_INTERVAL);
|
}, WAITING_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* There is a mini-state machine after entering STATES.makingConnection.
|
||||||
|
We make a request (which might immediately succeed, fail, or neither.
|
||||||
|
If we immediately fail, we tell the user.
|
||||||
|
Otherwise, we wait MAKING_CONNECTION_TIMEOUT. At that time, we poll until success or fail.
|
||||||
|
*/
|
||||||
|
var result, requestBody, pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request';
|
||||||
|
function connectionRequestCompleted() { // Final result is in. Do effects.
|
||||||
|
if (result.status === 'success') { // set earlier
|
||||||
|
if (!successfulHandshakeInjector) {
|
||||||
|
successfulHandshakeInjector = Audio.playSound(successfulHandshakeSound, {position: getHandPosition(MyAvatar, currentHand), volume: 0.5, localOnly: true});
|
||||||
|
} else {
|
||||||
|
successfulHandshakeInjector.restart();
|
||||||
|
}
|
||||||
|
Controller.triggerHapticPulse(HAPTIC_DATA.success.strength, HAPTIC_DATA.success.duration, handToHaptic(currentHand));
|
||||||
|
// don't change state (so animation continues while gripped)
|
||||||
|
// but do send a notification, by calling the slot that emits the signal for it
|
||||||
|
Window.makeConnection(true, result.connection.new_connection ? "You and " + result.connection.username + " are now connected!" : result.connection.username);
|
||||||
|
return;
|
||||||
|
} // failed
|
||||||
|
endHandshake();
|
||||||
|
debug("failing with result data", result);
|
||||||
|
// IWBNI we also did some fail sound/visual effect.
|
||||||
|
Window.makeConnection(false, result.connection);
|
||||||
|
}
|
||||||
|
var POLL_INTERVAL_MS = 200, POLL_LIMIT = 5;
|
||||||
|
function handleConnectionResponseAndMaybeRepeat(error, response) {
|
||||||
|
// If response is 'pending', set a short timeout to try again.
|
||||||
|
// If we fail other than pending, set result and immediately call connectionRequestCompleted.
|
||||||
|
// If we succceed, set result and call connectionRequestCompleted immediately (if we've been polling), and otherwise on a timeout.
|
||||||
|
if (response && (response.connection === 'pending')) {
|
||||||
|
debug(response, 'pollCount', pollCount);
|
||||||
|
if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long.
|
||||||
|
result = {status: 'error', connection: 'expired'};
|
||||||
|
connectionRequestCompleted();
|
||||||
|
} else { // poll
|
||||||
|
Script.setTimeout(function () {
|
||||||
|
request({
|
||||||
|
uri: requestUrl,
|
||||||
|
// N.B.: server gives bad request if we specify json content type, so don't do that.
|
||||||
|
body: requestBody
|
||||||
|
}, handleConnectionResponseAndMaybeRepeat);
|
||||||
|
}, POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
} else if (error || (response.status !== 'success')) {
|
||||||
|
debug('server fail', error, response.status);
|
||||||
|
result = error ? {status: 'error', connection: error} : response;
|
||||||
|
connectionRequestCompleted();
|
||||||
|
} else {
|
||||||
|
debug('server success', result);
|
||||||
|
result = response;
|
||||||
|
if (pollCount++) {
|
||||||
|
connectionRequestCompleted();
|
||||||
|
} else { // Wait for other guy, so that final succcess is at roughly the same time.
|
||||||
|
Script.setTimeout(connectionRequestCompleted, MAKING_CONNECTION_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this should be where we make the appropriate connection call. For now just make the
|
// this should be where we make the appropriate connection call. For now just make the
|
||||||
// visualization change.
|
// visualization change.
|
||||||
function makeConnection(id) {
|
function makeConnection(id) {
|
||||||
|
@ -484,18 +597,15 @@ function makeConnection(id) {
|
||||||
// continue the haptic background until the timeout fires. When we make calls, we will have an interval
|
// continue the haptic background until the timeout fires. When we make calls, we will have an interval
|
||||||
// probably, in which we do this.
|
// probably, in which we do this.
|
||||||
Controller.triggerHapticPulse(HAPTIC_DATA.background.strength, MAKING_CONNECTION_TIMEOUT, handToHaptic(currentHand));
|
Controller.triggerHapticPulse(HAPTIC_DATA.background.strength, MAKING_CONNECTION_TIMEOUT, handToHaptic(currentHand));
|
||||||
|
requestBody = {node_id: cleanId(MyAvatar.sessionUUID), proposed_node_id: cleanId(id)}; // for use when repeating
|
||||||
makingFriendsTimeout = Script.setTimeout(function () {
|
// This will immediately set response if successfull (e.g., the other guy got his request in first), or immediate failure,
|
||||||
if (!successfulHandshakeInjector) {
|
// and will otherwise poll (using the requestBody we just set).
|
||||||
successfulHandshakeInjector = Audio.playSound(successfulHandshakeSound, {position: getHandPosition(MyAvatar, currentHand), volume: 0.5, localOnly: true});
|
request({ //
|
||||||
} else {
|
uri: requestUrl,
|
||||||
successfulHandshakeInjector.restart();
|
method: 'POST',
|
||||||
}
|
json: true,
|
||||||
Controller.triggerHapticPulse(HAPTIC_DATA.success.strength, HAPTIC_DATA.success.duration, handToHaptic(currentHand));
|
body: {user_connection_request: requestBody}
|
||||||
// don't change state (so animation continues while gripped)
|
}, handleConnectionResponseAndMaybeRepeat);
|
||||||
// but do send a notification, by calling the slot that emits the signal for it
|
|
||||||
Window.makeConnection(true, "otherGuy"); // this is successful, unsucessful would be false, errorString
|
|
||||||
}, MAKING_CONNECTION_TIMEOUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we change states, start the connectionInterval where we check
|
// we change states, start the connectionInterval where we check
|
||||||
|
|
|
@ -548,7 +548,7 @@ function processingGif() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectionAdded(connectionName) {
|
function connectionAdded(connectionName) {
|
||||||
createNotification(wordWrap("Successfully connected to " + connectionName), NotificationType.CONNECTION);
|
createNotification(connectionName, NotificationType.CONNECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectionError(error) {
|
function connectionError(error) {
|
||||||
|
|
|
@ -334,75 +334,35 @@ function getProfilePicture(username, callback) { // callback(url) if successfull
|
||||||
function getAvailableConnections(domain, callback) { // callback([{usename, location}...]) if successfull. (Logs otherwise)
|
function getAvailableConnections(domain, callback) { // callback([{usename, location}...]) if successfull. (Logs otherwise)
|
||||||
// The back end doesn't do user connections yet. Fake it by getting all users that have made themselves accessible to us,
|
// The back end doesn't do user connections yet. Fake it by getting all users that have made themselves accessible to us,
|
||||||
// and pretending that they are all connections.
|
// and pretending that they are all connections.
|
||||||
function getData(cb) {
|
url = METAVERSE_BASE + '/api/v1/users?'
|
||||||
requestJSON(METAVERSE_BASE + '/api/v1/users?status=online', function (connectionsData) {
|
|
||||||
|
|
||||||
// The above does not include friend status. Fetch that separately.
|
|
||||||
requestJSON(METAVERSE_BASE + '/api/v1/user/friends', function (friendsData) {
|
|
||||||
var users = connectionsData.users || [], friends = friendsData.friends || [];
|
|
||||||
users.forEach(function (user) {
|
|
||||||
user.connection = (friends.indexOf(user.username) < 0) ? 'connection' : 'friend';
|
|
||||||
});
|
|
||||||
|
|
||||||
// The back end doesn't include the profile picture data, but we can add that here.
|
|
||||||
// For our current purposes, there's no need to be fancy and try to reduce latency by doing some number of requests in parallel,
|
|
||||||
// so these requests are all sequential.
|
|
||||||
function addPicture(index) {
|
|
||||||
if (index >= users.length) {
|
|
||||||
return cb(users);
|
|
||||||
}
|
|
||||||
var user = users[index];
|
|
||||||
getProfilePicture(user.username, function (url) {
|
|
||||||
user.profileUrl = url;
|
|
||||||
addPicture(index + 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
addPicture(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (domain) {
|
if (domain) {
|
||||||
// The back end doesn't keep sessionUUID in the location data yet. Fake it by finding the avatar closest to the path.
|
url += 'status=' + domain.slice(1, -1); // without curly braces
|
||||||
var positions = {};
|
} else {
|
||||||
AvatarList.getAvatarIdentifiers().forEach(function (id) {
|
url += 'filter=connections'; // regardless of whether online
|
||||||
positions[id || ''] = AvatarList.getAvatar(id).position; // Don't use null id as a key. Properties must be a string, and we don't want 'null'.
|
|
||||||
});
|
|
||||||
getData(function (users) {
|
|
||||||
// The endpoint in getData doesn't take a domain filter. So filter out the unwanted stuff now.
|
|
||||||
domain = domain.slice(1, -1); // without curly braces
|
|
||||||
users = users.filter(function (user) { return (user.location.domain || (user.location.root && user.location.root.domain) || {}).id === domain; });
|
|
||||||
|
|
||||||
// Now fill in the sessionUUID as if it were in the data all along.
|
|
||||||
users.forEach(function (user) {
|
|
||||||
var coordinates = user.location.path.match(/\/([^,]+)\,([^,]+),([^\/]+)\//);
|
|
||||||
if (coordinates) {
|
|
||||||
var position = {x: Number(coordinates[1]), y: Number(coordinates[2]), z: Number(coordinates[3])};
|
|
||||||
var none = 'not found', closestId = none, bestDistance = Infinity, distance, id;
|
|
||||||
for (id in positions) {
|
|
||||||
distance = Vec3.distance(position, positions[id]);
|
|
||||||
if (distance < bestDistance) {
|
|
||||||
closestId = id;
|
|
||||||
bestDistance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (closestId !== none) {
|
|
||||||
user.location.sessionUUID = closestId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(users);
|
|
||||||
});
|
|
||||||
} else { // We don't need to filter, nor add any sessionUUID data
|
|
||||||
getData(callback);
|
|
||||||
}
|
}
|
||||||
|
requestJSON(url, function (connectionsData) {
|
||||||
|
// The back end doesn't include the profile picture data, but we can add that here.
|
||||||
|
// For our current purposes, there's no need to be fancy and try to reduce latency by doing some number of requests in parallel,
|
||||||
|
// so these requests are all sequential.
|
||||||
|
var users = connectionsData.users;
|
||||||
|
function addPicture(index) {
|
||||||
|
if (index >= users.length) {
|
||||||
|
return callback(users);
|
||||||
|
}
|
||||||
|
var user = users[index];
|
||||||
|
getProfilePicture(user.username, function (url) {
|
||||||
|
user.profileUrl = url;
|
||||||
|
addPicture(index + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addPicture(0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConnectionData(domain) { // Update all the usernames that I am entitled to see, using my login but not dependent on canKick.
|
function getConnectionData(domain) { // Update all the usernames that I am entitled to see, using my login but not dependent on canKick.
|
||||||
function frob(user) { // get into the right format
|
function frob(user) { // get into the right format
|
||||||
return {
|
return {
|
||||||
sessionId: user.location.sessionUUID || '',
|
sessionId: user.location.node_id || '',
|
||||||
userName: user.username,
|
userName: user.username,
|
||||||
connection: user.connection,
|
connection: user.connection,
|
||||||
profileUrl: user.profileUrl,
|
profileUrl: user.profileUrl,
|
||||||
|
|
Loading…
Reference in a new issue