mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 09:18:45 +02:00
merge of Request module
This commit is contained in:
commit
55fdf35c4a
8 changed files with 434 additions and 397 deletions
|
@ -34,12 +34,13 @@ Column {
|
||||||
|
|
||||||
property string metaverseServerUrl: '';
|
property string metaverseServerUrl: '';
|
||||||
property string actions: 'snapshot';
|
property string actions: 'snapshot';
|
||||||
onActionsChanged: fillDestinations();
|
// sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick.
|
||||||
Component.onCompleted: fillDestinations();
|
Component.onCompleted: delay.start();
|
||||||
property string labelText: actions;
|
property string labelText: actions;
|
||||||
property string filter: '';
|
property string filter: '';
|
||||||
onFilterChanged: filterChoicesByText();
|
onFilterChanged: filterChoicesByText();
|
||||||
property var goFunction: null;
|
property var goFunction: null;
|
||||||
|
property var rpc: null;
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
ListModel { id: suggestions; }
|
ListModel { id: suggestions; }
|
||||||
|
@ -81,6 +82,20 @@ Column {
|
||||||
property var allStories: [];
|
property var allStories: [];
|
||||||
property var placeMap: ({}); // Used for making stacks.
|
property var placeMap: ({}); // Used for making stacks.
|
||||||
property int requestId: 0;
|
property int requestId: 0;
|
||||||
|
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
|
||||||
|
if (!error && (data.status === 'success')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!error) { // Create a message from the data
|
||||||
|
error = data.status + ': ' + data.error;
|
||||||
|
}
|
||||||
|
if (typeof(error) === 'string') { // Make a proper Error object
|
||||||
|
error = new Error(error);
|
||||||
|
}
|
||||||
|
error.message += ' in ' + url; // Include the url.
|
||||||
|
cb(error);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model
|
function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model
|
||||||
// If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness.
|
// If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness.
|
||||||
var options = [
|
var options = [
|
||||||
|
@ -93,8 +108,11 @@ Column {
|
||||||
];
|
];
|
||||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||||
var thisRequestId = ++requestId;
|
var thisRequestId = ++requestId;
|
||||||
getRequest(url, function (error, data) {
|
rpc('request', url, function (error, data) {
|
||||||
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
|
if (thisRequestId !== requestId) {
|
||||||
|
error = 'stale';
|
||||||
|
}
|
||||||
|
if (handleError(url, error, data, cb)) {
|
||||||
return; // abandon stale requests
|
return; // abandon stale requests
|
||||||
}
|
}
|
||||||
allStories = allStories.concat(data.user_stories.map(makeModelData));
|
allStories = allStories.concat(data.user_stories.map(makeModelData));
|
||||||
|
@ -107,14 +125,21 @@ Column {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
property var delay: Timer { // No setTimeout or nextTick in QML.
|
||||||
|
interval: 0;
|
||||||
|
onTriggered: fillDestinations();
|
||||||
|
}
|
||||||
function fillDestinations() { // Public
|
function fillDestinations() { // Public
|
||||||
|
function report(label, error) {
|
||||||
|
console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
|
||||||
|
}
|
||||||
var filter = makeFilteredStoryProcessor(), counter = 0;
|
var filter = makeFilteredStoryProcessor(), counter = 0;
|
||||||
allStories = [];
|
allStories = [];
|
||||||
suggestions.clear();
|
suggestions.clear();
|
||||||
placeMap = {};
|
placeMap = {};
|
||||||
getUserStoryPage(1, function (error) {
|
getUserStoryPage(1, function (error) {
|
||||||
allStories.slice(counter).forEach(filter);
|
allStories.slice(counter).forEach(filter);
|
||||||
console.log('user stories query', actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
|
report('user stories update', error);
|
||||||
root.visible = !!suggestions.count;
|
root.visible = !!suggestions.count;
|
||||||
}, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed.
|
}, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed.
|
||||||
allStories.forEach(function (story) {
|
allStories.forEach(function (story) {
|
||||||
|
@ -122,15 +147,19 @@ Column {
|
||||||
filter(story);
|
filter(story);
|
||||||
root.visible = !!suggestions.count;
|
root.visible = !!suggestions.count;
|
||||||
});
|
});
|
||||||
|
report('user stories');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function identity(x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
|
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
|
||||||
var words = filter.toUpperCase().split(/\s+/).filter(identity);
|
var words = filter.toUpperCase().split(/\s+/).filter(identity);
|
||||||
function suggestable(story) {
|
function suggestable(story) {
|
||||||
if (story.action === 'snapshot') {
|
if (story.action === 'snapshot') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
|
return story.place_name !== AddressManager.placename; // Not our entry, but do show other entry points to current domain.
|
||||||
}
|
}
|
||||||
function matches(story) {
|
function matches(story) {
|
||||||
if (!words.length) {
|
if (!words.length) {
|
||||||
|
|
|
@ -33,9 +33,27 @@ StackView {
|
||||||
property int cardWidth: 212;
|
property int cardWidth: 212;
|
||||||
property int cardHeight: 152;
|
property int cardHeight: 152;
|
||||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||||
|
|
||||||
property var tablet: null;
|
property var tablet: null;
|
||||||
|
|
||||||
|
// This version only implements rpc(method, parameters, callback(error, result)) calls initiated from here, not initiated from .js, nor "notifications".
|
||||||
|
property var rpcCalls: ({});
|
||||||
|
property var rpcCounter: 0;
|
||||||
|
signal sendToScript(var message);
|
||||||
|
function rpc(method, parameters, callback) {
|
||||||
|
rpcCalls[rpcCounter] = callback;
|
||||||
|
var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"};
|
||||||
|
sendToScript(message);
|
||||||
|
}
|
||||||
|
function fromScript(message) {
|
||||||
|
var callback = rpcCalls[message.id];
|
||||||
|
if (!callback) {
|
||||||
|
console.log('No callback for message fromScript', JSON.stringify(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete rpcCalls[message.id];
|
||||||
|
callback(message.error, message.result);
|
||||||
|
}
|
||||||
|
|
||||||
Component { id: tabletWebView; TabletWebView {} }
|
Component { id: tabletWebView; TabletWebView {} }
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateLocationText(false);
|
updateLocationText(false);
|
||||||
|
@ -54,7 +72,7 @@ StackView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function resetAfterTeleport() {
|
function resetAfterTeleport() {
|
||||||
//storyCardFrame.shown = root.shown = false;
|
//storyCardFrame.shown = root.shown = false;
|
||||||
}
|
}
|
||||||
function goCard(targetString) {
|
function goCard(targetString) {
|
||||||
|
@ -266,6 +284,7 @@ StackView {
|
||||||
actions: 'announcement';
|
actions: 'announcement';
|
||||||
filter: addressLine.text;
|
filter: addressLine.text;
|
||||||
goFunction: goCard;
|
goFunction: goCard;
|
||||||
|
rpc: root.rpc;
|
||||||
}
|
}
|
||||||
Feed {
|
Feed {
|
||||||
id: places;
|
id: places;
|
||||||
|
@ -278,6 +297,7 @@ StackView {
|
||||||
actions: 'concurrency';
|
actions: 'concurrency';
|
||||||
filter: addressLine.text;
|
filter: addressLine.text;
|
||||||
goFunction: goCard;
|
goFunction: goCard;
|
||||||
|
rpc: root.rpc;
|
||||||
}
|
}
|
||||||
Feed {
|
Feed {
|
||||||
id: snapshots;
|
id: snapshots;
|
||||||
|
@ -291,6 +311,7 @@ StackView {
|
||||||
actions: 'snapshot';
|
actions: 'snapshot';
|
||||||
filter: addressLine.text;
|
filter: addressLine.text;
|
||||||
goFunction: goCard;
|
goFunction: goCard;
|
||||||
|
rpc: root.rpc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,50 +351,6 @@ StackView {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
function identity(x) {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
|
|
||||||
if (!error && (data.status === 'success')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!error) { // Create a message from the data
|
|
||||||
error = data.status + ': ' + data.error;
|
|
||||||
}
|
|
||||||
if (typeof(error) === 'string') { // Make a proper Error object
|
|
||||||
error = new Error(error);
|
|
||||||
}
|
|
||||||
error.message += ' in ' + url; // Include the url.
|
|
||||||
cb(error);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLocationText(enteringAddress) {
|
function updateLocationText(enteringAddress) {
|
||||||
if (enteringAddress) {
|
if (enteringAddress) {
|
||||||
notice.text = "Go To a place, @user, path, or network address:";
|
notice.text = "Go To a place, @user, path, or network address:";
|
||||||
|
|
|
@ -166,6 +166,12 @@ void Deck::processFrames() {
|
||||||
if (!overLimit) {
|
if (!overLimit) {
|
||||||
auto nextFrameTime = nextClip->positionFrameTime();
|
auto nextFrameTime = nextClip->positionFrameTime();
|
||||||
nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position);
|
nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position);
|
||||||
|
if (nextInterval < 0) {
|
||||||
|
qCWarning(recordingLog) << "Unexpected nextInterval < 0 nextFrameTime:" << nextFrameTime
|
||||||
|
<< "_position:" << _position << "-- setting nextInterval to 0";
|
||||||
|
nextInterval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WANT_RECORDING_DEBUG
|
#ifdef WANT_RECORDING_DEBUG
|
||||||
qCDebug(recordingLog) << "Now " << _position;
|
qCDebug(recordingLog) << "Now " << _position;
|
||||||
qCDebug(recordingLog) << "Next frame time " << nextInterval;
|
qCDebug(recordingLog) << "Next frame time " << nextInterval;
|
||||||
|
|
|
@ -1801,15 +1801,15 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processStylus();
|
this.processStylus();
|
||||||
|
|
||||||
if (isInEditMode() && !this.isNearStylusTarget) {
|
if (isInEditMode() && !this.isNearStylusTarget && HMD.isHandControllerAvailable()) {
|
||||||
// Always showing lasers while in edit mode and hands/stylus is not active.
|
// Always showing lasers while in edit mode and hands/stylus is not active.
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||||
this.intersectionDistance = (rayPickInfo.entityID || rayPickInfo.overlayID) ? rayPickInfo.distance : 0;
|
this.intersectionDistance = (rayPickInfo.entityID || rayPickInfo.overlayID) ? rayPickInfo.distance : 0;
|
||||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
this.searchIndicatorOn(rayPickInfo.searchRay);
|
||||||
} else {
|
} else {
|
||||||
this.searchIndicatorOff();
|
this.searchIndicatorOff();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleLaserOnHomeButton = function(rayPickInfo) {
|
this.handleLaserOnHomeButton = function(rayPickInfo) {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
/*jslint vars:true, plusplus:true, forin:true*/
|
||||||
|
/*global Window, Script, Controller, MyAvatar, AvatarList, Entities, Messages, Audio, SoundCache, Account, UserActivityLogger, Vec3, Quat, XMLHttpRequest, location, print*/
|
||||||
//
|
//
|
||||||
// makeUserConnection.js
|
// makeUserConnection.js
|
||||||
// scripts/system
|
// scripts/system
|
||||||
|
@ -10,10 +12,10 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
var RequestModule = Script.require('./request.js');
|
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
var request = Script.require('../modules/request.js').request;
|
||||||
|
|
||||||
var LABEL = "makeUserConnection";
|
var LABEL = "makeUserConnection";
|
||||||
var MAX_AVATAR_DISTANCE = 0.2; // m
|
var MAX_AVATAR_DISTANCE = 0.2; // m
|
||||||
var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed
|
var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed
|
||||||
|
@ -25,12 +27,13 @@ var RequestModule = Script.require('./request.js');
|
||||||
MAKING_CONNECTION: 3
|
MAKING_CONNECTION: 3
|
||||||
};
|
};
|
||||||
var STATE_STRINGS = ["inactive", "waiting", "connecting", "makingConnection"];
|
var STATE_STRINGS = ["inactive", "waiting", "connecting", "makingConnection"];
|
||||||
|
var HAND_STRING_PROPERTY = 'hand'; // Used in our message protocol. IWBNI we changed it to handString, but that would break compatability.
|
||||||
var WAITING_INTERVAL = 100; // ms
|
var WAITING_INTERVAL = 100; // ms
|
||||||
var CONNECTING_INTERVAL = 100; // ms
|
var CONNECTING_INTERVAL = 100; // ms
|
||||||
var MAKING_CONNECTION_TIMEOUT = 800; // ms
|
var MAKING_CONNECTION_TIMEOUT = 800; // ms
|
||||||
var CONNECTING_TIME = 1600; // ms
|
var CONNECTING_TIME = 1600; // ms
|
||||||
var PARTICLE_RADIUS = 0.15; // m
|
var PARTICLE_RADIUS = 0.15; // m
|
||||||
var PARTICLE_ANGLE_INCREMENT = 360/45; // 1hz
|
var PARTICLE_ANGLE_INCREMENT = 360 / 45; // 1hz
|
||||||
var HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/4beat_sweep.wav";
|
var HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/4beat_sweep.wav";
|
||||||
var SUCCESSFUL_HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/3rdbeat_success_bell.wav";
|
var SUCCESSFUL_HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/3rdbeat_success_bell.wav";
|
||||||
var PREFERRER_HAND_JOINT_POSTFIX_ORDER = ['Middle1', 'Index1', ''];
|
var PREFERRER_HAND_JOINT_POSTFIX_ORDER = ['Middle1', 'Index1', ''];
|
||||||
|
@ -42,7 +45,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
var PARTICLE_EFFECT_PROPS = {
|
var PARTICLE_EFFECT_PROPS = {
|
||||||
"alpha": 0.8,
|
"alpha": 0.8,
|
||||||
"azimuthFinish": Math.PI,
|
"azimuthFinish": Math.PI,
|
||||||
"azimuthStart": -1*Math.PI,
|
"azimuthStart": -1 * Math.PI,
|
||||||
"emitRate": 500,
|
"emitRate": 500,
|
||||||
"emitSpeed": 0.0,
|
"emitSpeed": 0.0,
|
||||||
"emitterShouldTrail": 1,
|
"emitterShouldTrail": 1,
|
||||||
|
@ -59,10 +62,10 @@ var RequestModule = Script.require('./request.js');
|
||||||
"color": {"red": 255, "green": 255, "blue": 255},
|
"color": {"red": 255, "green": 255, "blue": 255},
|
||||||
"colorFinish": {"red": 0, "green": 164, "blue": 255},
|
"colorFinish": {"red": 0, "green": 164, "blue": 255},
|
||||||
"colorStart": {"red": 255, "green": 255, "blue": 255},
|
"colorStart": {"red": 255, "green": 255, "blue": 255},
|
||||||
"emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71},
|
"emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71},
|
||||||
"emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0},
|
"emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
"accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0},
|
"accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
"dimensions": {"x":0.05, "y": 0.05, "z": 0.05},
|
"dimensions": {"x": 0.05, "y": 0.05, "z": 0.05},
|
||||||
"type": "ParticleEffect"
|
"type": "ParticleEffect"
|
||||||
};
|
};
|
||||||
var MAKING_CONNECTION_PARTICLE_PROPS = {
|
var MAKING_CONNECTION_PARTICLE_PROPS = {
|
||||||
|
@ -71,7 +74,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
"alphaSpread": 0,
|
"alphaSpread": 0,
|
||||||
"alphaFinish": 0,
|
"alphaFinish": 0,
|
||||||
"azimuthFinish": Math.PI,
|
"azimuthFinish": Math.PI,
|
||||||
"azimuthStart": -1*Math.PI,
|
"azimuthStart": -1 * Math.PI,
|
||||||
"emitRate": 2000,
|
"emitRate": 2000,
|
||||||
"emitSpeed": 0.0,
|
"emitSpeed": 0.0,
|
||||||
"emitterShouldTrail": 1,
|
"emitterShouldTrail": 1,
|
||||||
|
@ -89,14 +92,14 @@ var RequestModule = Script.require('./request.js');
|
||||||
"color": {"red": 200, "green": 170, "blue": 255},
|
"color": {"red": 200, "green": 170, "blue": 255},
|
||||||
"colorFinish": {"red": 0, "green": 134, "blue": 255},
|
"colorFinish": {"red": 0, "green": 134, "blue": 255},
|
||||||
"colorStart": {"red": 185, "green": 222, "blue": 255},
|
"colorStart": {"red": 185, "green": 222, "blue": 255},
|
||||||
"emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71},
|
"emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71},
|
||||||
"emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0},
|
"emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
"accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0},
|
"accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0},
|
||||||
"dimensions": {"x":0.05, "y": 0.05, "z": 0.05},
|
"dimensions": {"x": 0.05, "y": 0.05, "z": 0.05},
|
||||||
"type": "ParticleEffect"
|
"type": "ParticleEffect"
|
||||||
};
|
};
|
||||||
|
|
||||||
var currentHand = undefined;
|
var currentHand;
|
||||||
var currentHandJointIndex = -1;
|
var currentHandJointIndex = -1;
|
||||||
var state = STATES.INACTIVE;
|
var state = STATES.INACTIVE;
|
||||||
var connectingInterval;
|
var connectingInterval;
|
||||||
|
@ -104,7 +107,6 @@ var RequestModule = Script.require('./request.js');
|
||||||
var makingConnectionTimeout;
|
var makingConnectionTimeout;
|
||||||
var animHandlerId;
|
var animHandlerId;
|
||||||
var connectingId;
|
var connectingId;
|
||||||
var connectingHandString;
|
|
||||||
var connectingHandJointIndex = -1;
|
var connectingHandJointIndex = -1;
|
||||||
var waitingList = {};
|
var waitingList = {};
|
||||||
var particleEffect;
|
var particleEffect;
|
||||||
|
@ -119,7 +121,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
|
|
||||||
function debug() {
|
function debug() {
|
||||||
var stateString = "<" + STATE_STRINGS[state] + ">";
|
var stateString = "<" + STATE_STRINGS[state] + ">";
|
||||||
var connecting = "[" + connectingId + "/" + connectingHandString + "]";
|
var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]";
|
||||||
print.apply(null, [].concat.apply([LABEL, stateString, JSON.stringify(waitingList), connecting],
|
print.apply(null, [].concat.apply([LABEL, stateString, JSON.stringify(waitingList), connecting],
|
||||||
[].map.call(arguments, JSON.stringify)));
|
[].map.call(arguments, JSON.stringify)));
|
||||||
}
|
}
|
||||||
|
@ -135,27 +137,19 @@ var RequestModule = Script.require('./request.js');
|
||||||
function handToString(hand) {
|
function handToString(hand) {
|
||||||
if (hand === Controller.Standard.RightHand) {
|
if (hand === Controller.Standard.RightHand) {
|
||||||
return "RightHand";
|
return "RightHand";
|
||||||
} else if (hand === Controller.Standard.LeftHand) {
|
}
|
||||||
|
if (hand === Controller.Standard.LeftHand) {
|
||||||
return "LeftHand";
|
return "LeftHand";
|
||||||
}
|
}
|
||||||
debug("handToString called without valid hand! value: ", hand);
|
debug("handToString called without valid hand! value: ", hand);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringToHand(hand) {
|
|
||||||
if (hand === "RightHand") {
|
|
||||||
return Controller.Standard.RightHand;
|
|
||||||
} else if (hand === "LeftHand") {
|
|
||||||
return Controller.Standard.LeftHand;
|
|
||||||
}
|
|
||||||
debug("stringToHand called with bad hand string:", hand);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handToHaptic(hand) {
|
function handToHaptic(hand) {
|
||||||
if (hand === Controller.Standard.RightHand) {
|
if (hand === Controller.Standard.RightHand) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (hand === Controller.Standard.LeftHand) {
|
}
|
||||||
|
if (hand === Controller.Standard.LeftHand) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
debug("handToHaptic called without a valid hand!");
|
debug("handToHaptic called without a valid hand!");
|
||||||
|
@ -181,13 +175,13 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
// This returns the ideal hand joint index for the avatar.
|
// This returns the ideal hand joint index for the avatar.
|
||||||
// [hand]middle1 -> [hand]index1 -> [hand]
|
// [handString]middle1 -> [handString]index1 -> [handString]
|
||||||
function getIdealHandJointIndex(avatar, hand) {
|
function getIdealHandJointIndex(avatar, handString) {
|
||||||
debug("got hand " + hand + " for avatar " + avatar.sessionUUID);
|
debug("get hand " + handString + " for avatar " + (avatar && avatar.sessionUUID));
|
||||||
var handString = handToString(hand);
|
var suffixIndex, jointName, jointIndex;
|
||||||
for (var i = 0; i < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; i++) {
|
for (suffixIndex = 0; suffixIndex < (avatar ? PREFERRER_HAND_JOINT_POSTFIX_ORDER.length : 0); suffixIndex++) {
|
||||||
var jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[i];
|
jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex];
|
||||||
var jointIndex = avatar.getJointIndex(jointName);
|
jointIndex = avatar.getJointIndex(jointName);
|
||||||
if (jointIndex !== -1) {
|
if (jointIndex !== -1) {
|
||||||
debug('found joint ' + jointName + ' (' + jointIndex + ')');
|
debug('found joint ' + jointName + ' (' + jointIndex + ')');
|
||||||
return jointIndex;
|
return jointIndex;
|
||||||
|
@ -201,26 +195,39 @@ var RequestModule = Script.require('./request.js');
|
||||||
function getHandPosition(avatar, handJointIndex) {
|
function getHandPosition(avatar, handJointIndex) {
|
||||||
if (handJointIndex === -1) {
|
if (handJointIndex === -1) {
|
||||||
debug("calling getHandPosition with no hand joint index! (returning avatar position but this is a BUG)");
|
debug("calling getHandPosition with no hand joint index! (returning avatar position but this is a BUG)");
|
||||||
debug(new Error().stack);
|
|
||||||
return avatar.position;
|
return avatar.position;
|
||||||
}
|
}
|
||||||
return avatar.getJointPosition(handJointIndex);
|
return avatar.getJointPosition(handJointIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shakeHandsAnimation(animationProperties) {
|
var animationData = {};
|
||||||
|
function updateAnimationData() {
|
||||||
// all we are doing here is moving the right hand to a spot
|
// all we are doing here is moving the right hand to a spot
|
||||||
// that is in front of and a bit above the hips. Basing how
|
// that is in front of and a bit above the hips. Basing how
|
||||||
// far in front as scaling with the avatar's height (say hips
|
// far in front as scaling with the avatar's height (say hips
|
||||||
// to head distance)
|
// to head distance)
|
||||||
var headIndex = MyAvatar.getJointIndex("Head");
|
var headIndex = MyAvatar.getJointIndex("Head");
|
||||||
var offset = 0.5; // default distance of hand in front of you
|
var offset = 0.5; // default distance of hand in front of you
|
||||||
var result = {};
|
|
||||||
if (headIndex) {
|
if (headIndex) {
|
||||||
offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
|
offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
|
||||||
}
|
}
|
||||||
result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
|
animationData.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
|
||||||
result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
|
animationData.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
|
||||||
return result;
|
}
|
||||||
|
function shakeHandsAnimation() {
|
||||||
|
return animationData;
|
||||||
|
}
|
||||||
|
function endHandshakeAnimation() {
|
||||||
|
if (animHandlerId) {
|
||||||
|
debug("removing animation");
|
||||||
|
animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function startHandshakeAnimation() {
|
||||||
|
endHandshakeAnimation(); // just in case order of press/unpress is broken
|
||||||
|
debug("adding animation");
|
||||||
|
updateAnimationData();
|
||||||
|
animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionFractionallyTowards(posA, posB, frac) {
|
function positionFractionallyTowards(posA, posB, frac) {
|
||||||
|
@ -246,11 +253,11 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcParticlePos(myHand, otherHand, otherOrientation, reset) {
|
function calcParticlePos(myHandPosition, otherHandPosition, otherOrientation, reset) {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
particleRotationAngle = 0.0;
|
particleRotationAngle = 0.0;
|
||||||
}
|
}
|
||||||
var position = positionFractionallyTowards(myHand, otherHand, 0.5);
|
var position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5);
|
||||||
particleRotationAngle += PARTICLE_ANGLE_INCREMENT; // about 0.5 hz
|
particleRotationAngle += PARTICLE_ANGLE_INCREMENT; // about 0.5 hz
|
||||||
var radius = Math.min(PARTICLE_RADIUS, PARTICLE_RADIUS * particleRotationAngle / 360);
|
var radius = Math.min(PARTICLE_RADIUS, PARTICLE_RADIUS * particleRotationAngle / 360);
|
||||||
var axis = Vec3.mix(Quat.getFront(MyAvatar.orientation), Quat.inverse(Quat.getFront(otherOrientation)), 0.5);
|
var axis = Vec3.mix(Quat.getFront(MyAvatar.orientation), Quat.inverse(Quat.getFront(otherOrientation)), 0.5);
|
||||||
|
@ -266,80 +273,78 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
var myHandPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
var myHandPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||||
var otherHand;
|
var otherHandPosition;
|
||||||
var otherOrientation;
|
var otherOrientation;
|
||||||
if (connectingId) {
|
if (connectingId) {
|
||||||
var other = AvatarList.getAvatar(connectingId);
|
var other = AvatarList.getAvatar(connectingId);
|
||||||
if (other) {
|
if (other) {
|
||||||
otherOrientation = other.orientation;
|
otherOrientation = other.orientation;
|
||||||
otherHand = getHandPosition(other, connectingHandJointIndex);
|
otherHandPosition = getHandPosition(other, connectingHandJointIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATES.WAITING:
|
case STATES.WAITING:
|
||||||
// no visualization while waiting
|
// no visualization while waiting
|
||||||
deleteParticleEffect();
|
deleteParticleEffect();
|
||||||
deleteMakeConnectionParticleEffect();
|
deleteMakeConnectionParticleEffect();
|
||||||
stopHandshakeSound();
|
stopHandshakeSound();
|
||||||
break;
|
break;
|
||||||
case STATES.CONNECTING:
|
case STATES.CONNECTING:
|
||||||
var particleProps = {};
|
var particleProps = {};
|
||||||
// put the position between the 2 hands, if we have a connectingId. This
|
// put the position between the 2 hands, if we have a connectingId. This
|
||||||
// helps define the plane in which the particles move.
|
// helps define the plane in which the particles move.
|
||||||
positionFractionallyTowards(myHandPosition, otherHand, 0.5);
|
positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5);
|
||||||
// now manage the rest of the entity
|
// now manage the rest of the entity
|
||||||
if (!particleEffect) {
|
if (!particleEffect) {
|
||||||
particleRotationAngle = 0.0;
|
particleRotationAngle = 0.0;
|
||||||
particleEmitRate = 500;
|
particleEmitRate = 500;
|
||||||
particleProps = PARTICLE_EFFECT_PROPS;
|
particleProps = PARTICLE_EFFECT_PROPS;
|
||||||
particleProps.isEmitting = 0;
|
particleProps.isEmitting = 0;
|
||||||
particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation);
|
particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation);
|
||||||
particleProps.parentID = MyAvatar.sessionUUID;
|
particleProps.parentID = MyAvatar.sessionUUID;
|
||||||
particleEffect = Entities.addEntity(particleProps, true);
|
particleEffect = Entities.addEntity(particleProps, true);
|
||||||
} else {
|
} else {
|
||||||
particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation);
|
particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation);
|
||||||
particleProps.isEmitting = 1;
|
particleProps.isEmitting = 1;
|
||||||
Entities.editEntity(particleEffect, particleProps);
|
Entities.editEntity(particleEffect, particleProps);
|
||||||
}
|
}
|
||||||
if (!makingConnectionParticleEffect) {
|
if (!makingConnectionParticleEffect) {
|
||||||
var props = MAKING_CONNECTION_PARTICLE_PROPS;
|
var props = MAKING_CONNECTION_PARTICLE_PROPS;
|
||||||
props.parentID = MyAvatar.sessionUUID;
|
props.parentID = MyAvatar.sessionUUID;
|
||||||
makingConnectionEmitRate = 2000;
|
makingConnectionEmitRate = 2000;
|
||||||
props.emitRate = makingConnectionEmitRate;
|
props.emitRate = makingConnectionEmitRate;
|
||||||
props.position = myHandPosition;
|
props.position = myHandPosition;
|
||||||
makingConnectionParticleEffect = Entities.addEntity(props, true);
|
makingConnectionParticleEffect = Entities.addEntity(props, true);
|
||||||
} else {
|
} else {
|
||||||
makingConnectionEmitRate *= 0.5;
|
makingConnectionEmitRate *= 0.5;
|
||||||
Entities.editEntity(makingConnectionParticleEffect, {
|
Entities.editEntity(makingConnectionParticleEffect, {
|
||||||
emitRate: makingConnectionEmitRate,
|
emitRate: makingConnectionEmitRate,
|
||||||
position: myHandPosition,
|
position: myHandPosition,
|
||||||
isEmitting: true
|
isEmitting: true
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case STATES.MAKING_CONNECTION:
|
|
||||||
particleEmitRate = Math.max(50, particleEmitRate * 0.5);
|
|
||||||
Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition});
|
|
||||||
Entities.editEntity(particleEffect, {
|
|
||||||
position: calcParticlePos(myHandPosition, otherHand, otherOrientation),
|
|
||||||
emitRate: particleEmitRate
|
|
||||||
});
|
});
|
||||||
break;
|
}
|
||||||
default:
|
break;
|
||||||
debug("unexpected state", state);
|
case STATES.MAKING_CONNECTION:
|
||||||
break;
|
particleEmitRate = Math.max(50, particleEmitRate * 0.5);
|
||||||
|
Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition});
|
||||||
|
Entities.editEntity(particleEffect, {
|
||||||
|
position: calcParticlePos(myHandPosition, otherHandPosition, otherOrientation),
|
||||||
|
emitRate: particleEmitRate
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debug("unexpected state", state);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNearby(id, hand) {
|
function isNearby() {
|
||||||
if (currentHand) {
|
if (currentHand) {
|
||||||
var handPos = getHandPosition(MyAvatar, currentHandJointIndex);
|
var handPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||||
var avatar = AvatarList.getAvatar(id);
|
var avatar = AvatarList.getAvatar(connectingId);
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
var otherHand = stringToHand(hand);
|
var distance = Vec3.distance(getHandPosition(avatar, connectingHandJointIndex), handPosition);
|
||||||
var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand);
|
|
||||||
var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPos);
|
|
||||||
return (distance < MAX_AVATAR_DISTANCE);
|
return (distance < MAX_AVATAR_DISTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,69 +352,90 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
function findNearestWaitingAvatar() {
|
function findNearestWaitingAvatar() {
|
||||||
var handPos = getHandPosition(MyAvatar, currentHandJointIndex);
|
var handPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||||
var minDistance = MAX_AVATAR_DISTANCE;
|
var minDistance = MAX_AVATAR_DISTANCE;
|
||||||
var nearestAvatar = {};
|
var nearestAvatar = {};
|
||||||
Object.keys(waitingList).forEach(function (identifier) {
|
Object.keys(waitingList).forEach(function (identifier) {
|
||||||
var avatar = AvatarList.getAvatar(identifier);
|
var avatar = AvatarList.getAvatar(identifier);
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
var hand = stringToHand(waitingList[identifier]);
|
var handJointIndex = waitingList[identifier];
|
||||||
var handJointIndex = getIdealHandJointIndex(avatar, hand);
|
var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition);
|
||||||
var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPos);
|
|
||||||
if (distance < minDistance) {
|
if (distance < minDistance) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar};
|
nearestAvatar = {avatarId: identifier, jointIndex: handJointIndex};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return nearestAvatar;
|
return nearestAvatar;
|
||||||
}
|
}
|
||||||
|
function messageSend(message) {
|
||||||
|
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||||
|
}
|
||||||
|
function handStringMessageSend(message) {
|
||||||
|
message[HAND_STRING_PROPERTY] = handToString(currentHand);
|
||||||
|
messageSend(message);
|
||||||
|
}
|
||||||
|
function setupCandidate() { // find the closest in-range avatar, send connection request, and return true. (Otherwise falsey)
|
||||||
|
var nearestAvatar = findNearestWaitingAvatar();
|
||||||
|
if (nearestAvatar.avatarId) {
|
||||||
|
connectingId = nearestAvatar.avatarId;
|
||||||
|
connectingHandJointIndex = nearestAvatar.jointIndex;
|
||||||
|
debug("sending connectionRequest to", connectingId);
|
||||||
|
handStringMessageSend({
|
||||||
|
key: "connectionRequest",
|
||||||
|
id: connectingId
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function clearConnecting() {
|
||||||
|
connectingId = undefined;
|
||||||
|
connectingHandJointIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookForWaitingAvatar() {
|
||||||
|
// we started with nobody close enough, but maybe I've moved
|
||||||
|
// or they did. Note that 2 people doing this race, so stop
|
||||||
|
// as soon as you have a connectingId (which means you got their
|
||||||
|
// message before noticing they were in range in this loop)
|
||||||
|
|
||||||
|
// just in case we re-enter before stopping
|
||||||
|
stopWaiting();
|
||||||
|
debug("started looking for waiting avatars");
|
||||||
|
waitingInterval = Script.setInterval(function () {
|
||||||
|
if (state === STATES.WAITING && !connectingId) {
|
||||||
|
setupCandidate();
|
||||||
|
} else {
|
||||||
|
// something happened, stop looking for avatars to connect
|
||||||
|
stopWaiting();
|
||||||
|
debug("stopped looking for waiting avatars");
|
||||||
|
}
|
||||||
|
}, WAITING_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request';
|
||||||
// As currently implemented, we select the closest waiting avatar (if close enough) and send
|
// As currently implemented, we select the closest waiting avatar (if close enough) and send
|
||||||
// them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a
|
// them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a
|
||||||
// connectionRequest. If the 2 people who want to connect are both somewhat out of range when they
|
// connectionRequest. If the 2 people who want to connect are both somewhat out of range when they
|
||||||
// initiate the shake, they will race to see who sends the connectionRequest after noticing the
|
// initiate the shake, they will race to see who sends the connectionRequest after noticing the
|
||||||
// waiting message. Either way, they will start connecting eachother at that point.
|
// waiting message. Either way, they will start connecting each other at that point.
|
||||||
function startHandshake(fromKeyboard) {
|
function startHandshake(fromKeyboard) {
|
||||||
|
|
||||||
if (fromKeyboard) {
|
if (fromKeyboard) {
|
||||||
debug("adding animation");
|
startHandshakeAnimation();
|
||||||
// just in case order of press/unpress is broken
|
|
||||||
if (animHandlerId) {
|
|
||||||
animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId);
|
|
||||||
}
|
|
||||||
animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []);
|
|
||||||
}
|
}
|
||||||
debug("starting handshake for", currentHand);
|
debug("starting handshake for", currentHand);
|
||||||
pollCount = 0;
|
pollCount = 0;
|
||||||
state = STATES.WAITING;
|
state = STATES.WAITING;
|
||||||
connectingId = undefined;
|
clearConnecting();
|
||||||
connectingHandString = undefined;
|
|
||||||
connectingHandJointIndex = -1;
|
|
||||||
// just in case
|
// just in case
|
||||||
stopWaiting();
|
stopWaiting();
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
stopMakingConnection();
|
stopMakingConnection();
|
||||||
|
if (!setupCandidate()) {
|
||||||
var nearestAvatar = findNearestWaitingAvatar();
|
|
||||||
if (nearestAvatar.avatar) {
|
|
||||||
connectingId = nearestAvatar.avatar;
|
|
||||||
connectingHandString = handToString(nearestAvatar.hand);
|
|
||||||
connectingHandJointIndex = getIdealHandJointIndex(nearestAvatar.avatarObject, nearestAvatar.hand);
|
|
||||||
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand);
|
|
||||||
debug("sending connectionRequest to", connectingId);
|
|
||||||
messageSend({
|
|
||||||
key: "connectionRequest",
|
|
||||||
id: connectingId,
|
|
||||||
hand: handToString(currentHand)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// send waiting message
|
// send waiting message
|
||||||
debug("sending waiting message");
|
debug("sending waiting message");
|
||||||
messageSend({
|
handStringMessageSend({
|
||||||
key: "waiting",
|
key: "waiting",
|
||||||
hand: handToString(currentHand)
|
|
||||||
});
|
});
|
||||||
lookForWaitingAvatar();
|
lookForWaitingAvatar();
|
||||||
}
|
}
|
||||||
|
@ -427,9 +453,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
// as we ignore the key release event when inactive. See updateTriggers
|
// as we ignore the key release event when inactive. See updateTriggers
|
||||||
// below.
|
// below.
|
||||||
state = STATES.INACTIVE;
|
state = STATES.INACTIVE;
|
||||||
connectingId = undefined;
|
clearConnecting();
|
||||||
connectingHandString = undefined;
|
|
||||||
connectingHandJointIndex = -1;
|
|
||||||
stopWaiting();
|
stopWaiting();
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
stopMakingConnection();
|
stopMakingConnection();
|
||||||
|
@ -439,13 +463,10 @@ var RequestModule = Script.require('./request.js');
|
||||||
key: "done"
|
key: "done"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (animHandlerId) {
|
endHandshakeAnimation();
|
||||||
debug("removing animation");
|
|
||||||
MyAvatar.removeAnimationStateHandler(animHandlerId);
|
|
||||||
}
|
|
||||||
// No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us
|
// No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us
|
||||||
// in a weird state.
|
// in a weird state.
|
||||||
RequestModule.request({ uri: requestUrl, method: 'DELETE' }, debug);
|
request({ uri: requestUrl, method: 'DELETE' }, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTriggers(value, fromKeyboard, hand) {
|
function updateTriggers(value, fromKeyboard, hand) {
|
||||||
|
@ -453,19 +474,16 @@ var RequestModule = Script.require('./request.js');
|
||||||
debug("currentHand", currentHand, "ignoring messages from", hand);
|
debug("currentHand", currentHand, "ignoring messages from", hand);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!currentHand) {
|
currentHand = hand;
|
||||||
currentHand = hand;
|
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton.
|
||||||
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand);
|
|
||||||
}
|
|
||||||
// ok now, we are either initiating or quitting...
|
// ok now, we are either initiating or quitting...
|
||||||
var isGripping = value > GRIP_MIN;
|
var isGripping = value > GRIP_MIN;
|
||||||
if (isGripping) {
|
if (isGripping) {
|
||||||
debug("updateTriggers called - gripping", handToString(hand));
|
debug("updateTriggers called - gripping", handToString(hand));
|
||||||
if (state !== STATES.INACTIVE) {
|
if (state !== STATES.INACTIVE) {
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
startHandshake(fromKeyboard);
|
|
||||||
}
|
}
|
||||||
|
startHandshake(fromKeyboard);
|
||||||
} else {
|
} else {
|
||||||
// TODO: should we end handshake even when inactive? Ponder
|
// TODO: should we end handshake even when inactive? Ponder
|
||||||
debug("updateTriggers called -- no longer gripping", handToString(hand));
|
debug("updateTriggers called -- no longer gripping", handToString(hand));
|
||||||
|
@ -477,47 +495,12 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function messageSend(message) {
|
|
||||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookForWaitingAvatar() {
|
|
||||||
// we started with nobody close enough, but maybe I've moved
|
|
||||||
// or they did. Note that 2 people doing this race, so stop
|
|
||||||
// as soon as you have a connectingId (which means you got their
|
|
||||||
// message before noticing they were in range in this loop)
|
|
||||||
|
|
||||||
// just in case we re-enter before stopping
|
|
||||||
stopWaiting();
|
|
||||||
debug("started looking for waiting avatars");
|
|
||||||
waitingInterval = Script.setInterval(function () {
|
|
||||||
if (state === STATES.WAITING && !connectingId) {
|
|
||||||
// find the closest in-range avatar, and send connection request
|
|
||||||
var nearestAvatar = findNearestWaitingAvatar();
|
|
||||||
if (nearestAvatar.avatar) {
|
|
||||||
connectingId = nearestAvatar.avatar;
|
|
||||||
connectingHandString = handToString(nearestAvatar.hand);
|
|
||||||
debug("sending connectionRequest to", connectingId);
|
|
||||||
messageSend({
|
|
||||||
key: "connectionRequest",
|
|
||||||
id: connectingId,
|
|
||||||
hand: handToString(currentHand)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// something happened, stop looking for avatars to connect
|
|
||||||
stopWaiting();
|
|
||||||
debug("stopped looking for waiting avatars");
|
|
||||||
}
|
|
||||||
}, WAITING_INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There is a mini-state machine after entering STATES.makingConnection.
|
/* There is a mini-state machine after entering STATES.makingConnection.
|
||||||
We make a request (which might immediately succeed, fail, or neither.
|
We make a request (which might immediately succeed, fail, or neither.
|
||||||
If we immediately fail, we tell the user.
|
If we immediately fail, we tell the user.
|
||||||
Otherwise, we wait MAKING_CONNECTION_TIMEOUT. At that time, we poll until success or fail.
|
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';
|
var result, requestBody;
|
||||||
function connectionRequestCompleted() { // Final result is in. Do effects.
|
function connectionRequestCompleted() { // Final result is in. Do effects.
|
||||||
if (result.status === 'success') { // set earlier
|
if (result.status === 'success') { // set earlier
|
||||||
if (!successfulHandshakeInjector) {
|
if (!successfulHandshakeInjector) {
|
||||||
|
@ -533,16 +516,37 @@ var RequestModule = Script.require('./request.js');
|
||||||
handToHaptic(currentHand));
|
handToHaptic(currentHand));
|
||||||
// don't change state (so animation continues while gripped)
|
// don't change state (so animation continues while gripped)
|
||||||
// but do send a notification, by calling the slot that emits the signal for it
|
// but do send a notification, by calling the slot that emits the signal for it
|
||||||
Window.makeConnection(true, result.connection.new_connection ?
|
Window.makeConnection(true,
|
||||||
"You and " + result.connection.username + " are now connected!" : result.connection.username);
|
result.connection.new_connection ?
|
||||||
UserActivityLogger.makeUserConnection(connectingId, true, result.connection.new_connection ?
|
"You and " + result.connection.username + " are now connected!" :
|
||||||
"new connection" : "already connected");
|
result.connection.username);
|
||||||
|
UserActivityLogger.makeUserConnection(connectingId,
|
||||||
|
true,
|
||||||
|
result.connection.new_connection ?
|
||||||
|
"new connection" :
|
||||||
|
"already connected");
|
||||||
return;
|
return;
|
||||||
} // failed
|
} // failed
|
||||||
endHandshake();
|
endHandshake();
|
||||||
debug("failing with result data", result);
|
debug("failing with result data", result);
|
||||||
// IWBNI we also did some fail sound/visual effect.
|
// IWBNI we also did some fail sound/visual effect.
|
||||||
Window.makeConnection(false, result.connection);
|
Window.makeConnection(false, result.connection);
|
||||||
|
if (Account.isLoggedIn()) { // Give extra failure info
|
||||||
|
request(location.metaverseServerUrl + '/api/v1/users/' + Account.username + '/location', function (error, response) {
|
||||||
|
var message = '';
|
||||||
|
if (error || response.status !== 'success') {
|
||||||
|
message = 'Unable to get location.';
|
||||||
|
} else if (!response.data || !response.data.location) {
|
||||||
|
message = "Unexpected location value: " + JSON.stringify(response);
|
||||||
|
} else if (response.data.location.node_id !== cleanId(MyAvatar.sessionUUID)) {
|
||||||
|
message = 'Session identification does not match database. Maybe you are logged in on another machine? That would prevent handshakes.' + JSON.stringify(response) + MyAvatar.sessionUUID;
|
||||||
|
}
|
||||||
|
if (message) {
|
||||||
|
Window.makeConnection(false, message);
|
||||||
|
}
|
||||||
|
debug("account location:", message || 'ok');
|
||||||
|
});
|
||||||
|
}
|
||||||
UserActivityLogger.makeUserConnection(connectingId, false, result.connection);
|
UserActivityLogger.makeUserConnection(connectingId, false, result.connection);
|
||||||
}
|
}
|
||||||
// This is a bit fragile - but to account for skew in when people actually create the
|
// This is a bit fragile - but to account for skew in when people actually create the
|
||||||
|
@ -559,11 +563,11 @@ var RequestModule = Script.require('./request.js');
|
||||||
debug(response, 'pollCount', pollCount);
|
debug(response, 'pollCount', pollCount);
|
||||||
if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long.
|
if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long.
|
||||||
debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT');
|
debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT');
|
||||||
result = {status: 'error', connection: 'expired'};
|
result = {status: 'error', connection: 'No logged-in partner found.'};
|
||||||
connectionRequestCompleted();
|
connectionRequestCompleted();
|
||||||
} else { // poll
|
} else { // poll
|
||||||
Script.setTimeout(function () {
|
Script.setTimeout(function () {
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: requestUrl,
|
uri: requestUrl,
|
||||||
// N.B.: server gives bad request if we specify json content type, so don't do that.
|
// N.B.: server gives bad request if we specify json content type, so don't do that.
|
||||||
body: requestBody
|
body: requestBody
|
||||||
|
@ -589,8 +593,6 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be where we make the appropriate connection call. For now just make the
|
|
||||||
// visualization change.
|
|
||||||
function makeConnection(id) {
|
function makeConnection(id) {
|
||||||
// send done to let the connection know you have made connection.
|
// send done to let the connection know you have made connection.
|
||||||
messageSend({
|
messageSend({
|
||||||
|
@ -600,8 +602,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
|
|
||||||
state = STATES.MAKING_CONNECTION;
|
state = STATES.MAKING_CONNECTION;
|
||||||
|
|
||||||
// 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.
|
||||||
// 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
|
requestBody = {'node_id': cleanId(MyAvatar.sessionUUID), 'proposed_node_id': cleanId(id)}; // for use when repeating
|
||||||
|
|
||||||
|
@ -615,26 +616,27 @@ var RequestModule = Script.require('./request.js');
|
||||||
|
|
||||||
// This will immediately set response if successful (e.g., the other guy got his request in first),
|
// This will immediately set response if successful (e.g., the other guy got his request in first),
|
||||||
// or immediate failure, and will otherwise poll (using the requestBody we just set).
|
// or immediate failure, and will otherwise poll (using the requestBody we just set).
|
||||||
RequestModule.request({ //
|
request({
|
||||||
uri: requestUrl,
|
uri: requestUrl,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
json: true,
|
json: true,
|
||||||
body: {'user_connection_request': requestBody}
|
body: {'user_connection_request': requestBody}
|
||||||
}, handleConnectionResponseAndMaybeRepeat);
|
}, handleConnectionResponseAndMaybeRepeat);
|
||||||
}
|
}
|
||||||
|
function setupConnecting(id, jointIndex) {
|
||||||
|
connectingId = id;
|
||||||
|
connectingHandJointIndex = jointIndex;
|
||||||
|
}
|
||||||
|
|
||||||
// we change states, start the connectionInterval where we check
|
// we change states, start the connectionInterval where we check
|
||||||
// to be sure the hand is still close enough. If not, we terminate
|
// to be sure the hand is still close enough. If not, we terminate
|
||||||
// the interval, go back to the waiting state. If we make it
|
// the interval, go back to the waiting state. If we make it
|
||||||
// the entire CONNECTING_TIME, we make the connection.
|
// the entire CONNECTING_TIME, we make the connection.
|
||||||
function startConnecting(id, hand) {
|
function startConnecting(id, jointIndex) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
debug("connecting", id, "hand", hand);
|
debug("connecting", id, "hand", jointIndex);
|
||||||
// do we need to do this?
|
// do we need to do this?
|
||||||
connectingId = id;
|
setupConnecting(id, jointIndex);
|
||||||
connectingHandString = hand;
|
|
||||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
|
||||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
|
||||||
state = STATES.CONNECTING;
|
state = STATES.CONNECTING;
|
||||||
|
|
||||||
// play sound
|
// play sound
|
||||||
|
@ -649,10 +651,9 @@ var RequestModule = Script.require('./request.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
// send message that we are connecting with them
|
// send message that we are connecting with them
|
||||||
messageSend({
|
handStringMessageSend({
|
||||||
key: "connecting",
|
key: "connecting",
|
||||||
id: id,
|
id: id
|
||||||
hand: handToString(currentHand)
|
|
||||||
});
|
});
|
||||||
Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand));
|
Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand));
|
||||||
|
|
||||||
|
@ -663,7 +664,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
if (state !== STATES.CONNECTING) {
|
if (state !== STATES.CONNECTING) {
|
||||||
debug("stopping connecting interval, state changed");
|
debug("stopping connecting interval, state changed");
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
} else if (!isNearby(id, hand)) {
|
} else if (!isNearby()) {
|
||||||
// gotta go back to waiting
|
// gotta go back to waiting
|
||||||
debug(id, "moved, back to waiting");
|
debug(id, "moved, back to waiting");
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
|
@ -671,7 +672,7 @@ var RequestModule = Script.require('./request.js');
|
||||||
key: "done"
|
key: "done"
|
||||||
});
|
});
|
||||||
startHandshake();
|
startHandshake();
|
||||||
} else if (count > CONNECTING_TIME/CONNECTING_INTERVAL) {
|
} else if (count > CONNECTING_TIME / CONNECTING_INTERVAL) {
|
||||||
debug("made connection with " + id);
|
debug("made connection with " + id);
|
||||||
makeConnection(id);
|
makeConnection(id);
|
||||||
stopConnecting();
|
stopConnecting();
|
||||||
|
@ -697,140 +698,120 @@ var RequestModule = Script.require('./request.js');
|
||||||
| ---------- (done) ---------> |
|
| ---------- (done) ---------> |
|
||||||
*/
|
*/
|
||||||
function messageHandler(channel, messageString, senderID) {
|
function messageHandler(channel, messageString, senderID) {
|
||||||
|
var message = {};
|
||||||
|
function exisitingOrSearchedJointIndex() { // If this is a new connectingId, we'll need to find the jointIndex
|
||||||
|
return connectingId ? connectingHandJointIndex : getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]);
|
||||||
|
}
|
||||||
if (channel !== MESSAGE_CHANNEL) {
|
if (channel !== MESSAGE_CHANNEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var message = {};
|
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(messageString);
|
message = JSON.parse(messageString);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debug(e);
|
debug(e);
|
||||||
}
|
}
|
||||||
switch (message.key) {
|
switch (message.key) {
|
||||||
case "waiting":
|
case "waiting":
|
||||||
// add this guy to waiting object. Any other message from this person will
|
// add this guy to waiting object. Any other message from this person will remove it from the list
|
||||||
// remove it from the list
|
waitingList[senderID] = getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]);
|
||||||
waitingList[senderID] = message.hand;
|
break;
|
||||||
break;
|
case "connectionRequest":
|
||||||
case "connectionRequest":
|
delete waitingList[senderID];
|
||||||
delete waitingList[senderID];
|
if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) {
|
||||||
if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID &&
|
// you were waiting for a connection request, so send the ack. Or, you and the other
|
||||||
(!connectingId || connectingId === senderID)) {
|
// guy raced and both send connectionRequests. Handle that too
|
||||||
// you were waiting for a connection request, so send the ack. Or, you and the other
|
setupConnecting(senderID, exisitingOrSearchedJointIndex());
|
||||||
// guy raced and both send connectionRequests. Handle that too
|
handStringMessageSend({
|
||||||
connectingId = senderID;
|
key: "connectionAck",
|
||||||
connectingHandString = message.hand;
|
id: senderID,
|
||||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
});
|
||||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
} else if (state === STATES.WAITING && connectingId === senderID) {
|
||||||
messageSend({
|
// the person you are trying to connect sent a request to someone else. See the
|
||||||
key: "connectionAck",
|
// if statement above. So, don't cry, just start the handshake over again
|
||||||
id: senderID,
|
startHandshake();
|
||||||
hand: handToString(currentHand)
|
}
|
||||||
});
|
break;
|
||||||
} else if (state === STATES.WAITING && connectingId === senderID) {
|
case "connectionAck":
|
||||||
// the person you are trying to connect sent a request to someone else. See the
|
delete waitingList[senderID];
|
||||||
// if statement above. So, don't cry, just start the handshake over again
|
if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) {
|
||||||
|
if (message.id === MyAvatar.sessionUUID) {
|
||||||
|
stopWaiting();
|
||||||
|
startConnecting(senderID, exisitingOrSearchedJointIndex());
|
||||||
|
} else if (connectingId) {
|
||||||
|
// this is for someone else (we lost race in connectionRequest),
|
||||||
|
// so lets start over
|
||||||
startHandshake();
|
startHandshake();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case "connectionAck":
|
// TODO: check to see if we are waiting for this but the person we are connecting sent it to
|
||||||
delete waitingList[senderID];
|
// someone else, and try again
|
||||||
if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) {
|
break;
|
||||||
if (message.id === MyAvatar.sessionUUID) {
|
case "connecting":
|
||||||
// start connecting...
|
delete waitingList[senderID];
|
||||||
connectingId = senderID;
|
if (state === STATES.WAITING && senderID === connectingId) {
|
||||||
connectingHandString = message.hand;
|
if (message.id !== MyAvatar.sessionUUID) {
|
||||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
// the person we were trying to connect is connecting to someone else
|
||||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
// so try again
|
||||||
stopWaiting();
|
startHandshake();
|
||||||
startConnecting(senderID, connectingHandString);
|
break;
|
||||||
} else if (connectingId) {
|
}
|
||||||
// this is for someone else (we lost race in connectionRequest),
|
startConnecting(senderID, connectingHandJointIndex);
|
||||||
// so lets start over
|
}
|
||||||
|
break;
|
||||||
|
case "done":
|
||||||
|
delete waitingList[senderID];
|
||||||
|
if (state === STATES.CONNECTING && connectingId === senderID) {
|
||||||
|
// if they are done, and didn't connect us, terminate our
|
||||||
|
// connecting
|
||||||
|
if (message.connectionId !== MyAvatar.sessionUUID) {
|
||||||
|
stopConnecting();
|
||||||
|
// now just call startHandshake. Should be ok to do so without a
|
||||||
|
// value for isKeyboard, as we should not change the animation
|
||||||
|
// state anyways (if any)
|
||||||
|
startHandshake();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if waiting or inactive, lets clear the connecting id. If in makingConnection,
|
||||||
|
// do nothing
|
||||||
|
if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) {
|
||||||
|
clearConnecting();
|
||||||
|
if (state !== STATES.INACTIVE) {
|
||||||
startHandshake();
|
startHandshake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: check to see if we are waiting for this but the person we are connecting sent it to
|
}
|
||||||
// someone else, and try again
|
break;
|
||||||
break;
|
default:
|
||||||
case "connecting":
|
debug("unknown message", message);
|
||||||
delete waitingList[senderID];
|
break;
|
||||||
if (state === STATES.WAITING && senderID === connectingId) {
|
|
||||||
// temporary logging
|
|
||||||
if (connectingHandString !== message.hand) {
|
|
||||||
debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand);
|
|
||||||
}
|
|
||||||
connectingHandString = message.hand;
|
|
||||||
if (message.id !== MyAvatar.sessionUUID) {
|
|
||||||
// the person we were trying to connect is connecting to someone else
|
|
||||||
// so try again
|
|
||||||
startHandshake();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
startConnecting(senderID, message.hand);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "done":
|
|
||||||
delete waitingList[senderID];
|
|
||||||
if (state === STATES.CONNECTING && connectingId === senderID) {
|
|
||||||
// if they are done, and didn't connect us, terminate our
|
|
||||||
// connecting
|
|
||||||
if (message.connectionId !== MyAvatar.sessionUUID) {
|
|
||||||
stopConnecting();
|
|
||||||
// now just call startHandshake. Should be ok to do so without a
|
|
||||||
// value for isKeyboard, as we should not change the animation
|
|
||||||
// state anyways (if any)
|
|
||||||
startHandshake();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if waiting or inactive, lets clear the connecting id. If in makingConnection,
|
|
||||||
// do nothing
|
|
||||||
if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) {
|
|
||||||
connectingId = undefined;
|
|
||||||
connectingHandString = undefined;
|
|
||||||
connectingHandJointIndex = -1;
|
|
||||||
if (state !== STATES.INACTIVE) {
|
|
||||||
startHandshake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
debug("unknown message", message);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Messages.subscribe(MESSAGE_CHANNEL);
|
Messages.subscribe(MESSAGE_CHANNEL);
|
||||||
Messages.messageReceived.connect(messageHandler);
|
Messages.messageReceived.connect(messageHandler);
|
||||||
|
|
||||||
|
|
||||||
function makeGripHandler(hand, animate) {
|
function makeGripHandler(hand, animate) {
|
||||||
// determine if we are gripping or un-gripping
|
// determine if we are gripping or un-gripping
|
||||||
if (animate) {
|
if (animate) {
|
||||||
return function(value) {
|
return function (value) {
|
||||||
updateTriggers(value, true, hand);
|
updateTriggers(value, true, hand);
|
||||||
};
|
};
|
||||||
|
|
||||||
} else {
|
|
||||||
return function (value) {
|
|
||||||
updateTriggers(value, false, hand);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
return function (value) {
|
||||||
|
updateTriggers(value, false, hand);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyPressEvent(event) {
|
function keyPressEvent(event) {
|
||||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl &&
|
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) {
|
||||||
!event.isAlt) {
|
|
||||||
updateTriggers(1.0, true, Controller.Standard.RightHand);
|
updateTriggers(1.0, true, Controller.Standard.RightHand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function keyReleaseEvent(event) {
|
function keyReleaseEvent(event) {
|
||||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl &&
|
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) {
|
||||||
!event.isAlt) {
|
|
||||||
updateTriggers(0.0, true, Controller.Standard.RightHand);
|
updateTriggers(0.0, true, Controller.Standard.RightHand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
var RequestModule = Script.require('./request.js');
|
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
var request = Script.require('../modules/request.js').request;
|
||||||
|
|
||||||
var populateNearbyUserList, color, textures, removeOverlays,
|
var populateNearbyUserList, color, textures, removeOverlays,
|
||||||
controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged,
|
controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged,
|
||||||
receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL,
|
receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL,
|
||||||
|
@ -273,7 +273,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
||||||
break;
|
break;
|
||||||
case 'removeConnection':
|
case 'removeConnection':
|
||||||
connectionUserName = message.params;
|
connectionUserName = message.params;
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: METAVERSE_BASE + '/api/v1/user/connections/' + connectionUserName,
|
uri: METAVERSE_BASE + '/api/v1/user/connections/' + connectionUserName,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
}, function (error, response) {
|
}, function (error, response) {
|
||||||
|
@ -287,7 +287,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
||||||
|
|
||||||
case 'removeFriend':
|
case 'removeFriend':
|
||||||
friendUserName = message.params;
|
friendUserName = message.params;
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: METAVERSE_BASE + '/api/v1/user/friends/' + friendUserName,
|
uri: METAVERSE_BASE + '/api/v1/user/friends/' + friendUserName,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
}, function (error, response) {
|
}, function (error, response) {
|
||||||
|
@ -300,7 +300,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
||||||
break
|
break
|
||||||
case 'addFriend':
|
case 'addFriend':
|
||||||
friendUserName = message.params;
|
friendUserName = message.params;
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: METAVERSE_BASE + '/api/v1/user/friends',
|
uri: METAVERSE_BASE + '/api/v1/user/friends',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
json: true,
|
json: true,
|
||||||
|
@ -335,7 +335,7 @@ function updateUser(data) {
|
||||||
var METAVERSE_BASE = location.metaverseServerUrl;
|
var METAVERSE_BASE = location.metaverseServerUrl;
|
||||||
|
|
||||||
function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise.
|
function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise.
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: url
|
uri: url
|
||||||
}, function (error, response) {
|
}, function (error, response) {
|
||||||
if (error || (response.status !== 'success')) {
|
if (error || (response.status !== 'success')) {
|
||||||
|
@ -347,7 +347,7 @@ function requestJSON(url, callback) { // callback(data) if successfull. Logs oth
|
||||||
}
|
}
|
||||||
function getProfilePicture(username, callback) { // callback(url) if successfull. (Logs otherwise)
|
function getProfilePicture(username, callback) { // callback(url) if successfull. (Logs otherwise)
|
||||||
// FIXME Prototype scrapes profile picture. We should include in user status, and also make available somewhere for myself
|
// FIXME Prototype scrapes profile picture. We should include in user status, and also make available somewhere for myself
|
||||||
RequestModule.request({
|
request({
|
||||||
uri: METAVERSE_BASE + '/users/' + username
|
uri: METAVERSE_BASE + '/users/' + username
|
||||||
}, function (error, html) {
|
}, function (error, html) {
|
||||||
var matched = !error && html.match(/img class="users-img" src="([^"]*)"/);
|
var matched = !error && html.match(/img class="users-img" src="([^"]*)"/);
|
||||||
|
@ -676,7 +676,6 @@ function startup() {
|
||||||
activeIcon: "icons/tablet-icons/people-a.svg",
|
activeIcon: "icons/tablet-icons/people-a.svg",
|
||||||
sortOrder: 7
|
sortOrder: 7
|
||||||
});
|
});
|
||||||
tablet.fromQml.connect(fromQml);
|
|
||||||
button.clicked.connect(onTabletButtonClicked);
|
button.clicked.connect(onTabletButtonClicked);
|
||||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||||
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
Users.usernameFromIDReply.connect(usernameFromIDReply);
|
||||||
|
@ -742,8 +741,23 @@ function onTabletButtonClicked() {
|
||||||
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var hasEventBridge = false;
|
||||||
|
function wireEventBridge(on) {
|
||||||
|
if (on) {
|
||||||
|
if (!hasEventBridge) {
|
||||||
|
tablet.fromQml.connect(fromQml);
|
||||||
|
hasEventBridge = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasEventBridge) {
|
||||||
|
tablet.fromQml.disconnect(fromQml);
|
||||||
|
hasEventBridge = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onTabletScreenChanged(type, url) {
|
function onTabletScreenChanged(type, url) {
|
||||||
|
wireEventBridge(shouldActivateButton);
|
||||||
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||||
button.editProperties({isActive: shouldActivateButton});
|
button.editProperties({isActive: shouldActivateButton});
|
||||||
shouldActivateButton = false;
|
shouldActivateButton = false;
|
||||||
|
|
|
@ -30,40 +30,6 @@
|
||||||
text: buttonName,
|
text: buttonName,
|
||||||
sortOrder: 8
|
sortOrder: 8
|
||||||
});
|
});
|
||||||
function messagesWaiting(isWaiting) {
|
|
||||||
button.editProperties({
|
|
||||||
icon: isWaiting ? WAITING_ICON : NORMAL_ICON
|
|
||||||
// No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClicked() {
|
|
||||||
if (onGotoScreen) {
|
|
||||||
// for toolbar-mode: go back to home screen, this will close the window.
|
|
||||||
tablet.gotoHomeScreen();
|
|
||||||
} else {
|
|
||||||
shouldActivateButton = true;
|
|
||||||
tablet.loadQMLSource(gotoQmlSource);
|
|
||||||
onGotoScreen = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onScreenChanged(type, url) {
|
|
||||||
ignore(type);
|
|
||||||
if (url === gotoQmlSource) {
|
|
||||||
onGotoScreen = true;
|
|
||||||
shouldActivateButton = true;
|
|
||||||
button.editProperties({isActive: shouldActivateButton});
|
|
||||||
messagesWaiting(false);
|
|
||||||
} else {
|
|
||||||
shouldActivateButton = false;
|
|
||||||
onGotoScreen = false;
|
|
||||||
button.editProperties({isActive: shouldActivateButton});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button.clicked.connect(onClicked);
|
|
||||||
tablet.screenChanged.connect(onScreenChanged);
|
|
||||||
|
|
||||||
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
|
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
|
||||||
var httpRequest = new XMLHttpRequest(), key;
|
var httpRequest = new XMLHttpRequest(), key;
|
||||||
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
||||||
|
@ -112,6 +78,70 @@
|
||||||
httpRequest.open(options.method, options.uri, true);
|
httpRequest.open(options.method, options.uri, true);
|
||||||
httpRequest.send(options.body);
|
httpRequest.send(options.body);
|
||||||
}
|
}
|
||||||
|
function fromQml(message) {
|
||||||
|
var response = {id: message.id, jsonrpc: "2.0"};
|
||||||
|
switch (message.method) {
|
||||||
|
case 'request':
|
||||||
|
request(message.params, function (error, data) {
|
||||||
|
response.error = error;
|
||||||
|
response.result = data;
|
||||||
|
tablet.sendToQml(response);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
response.error = {message: 'Unrecognized message', data: message};
|
||||||
|
}
|
||||||
|
tablet.sendToQml(response);
|
||||||
|
}
|
||||||
|
function messagesWaiting(isWaiting) {
|
||||||
|
button.editProperties({
|
||||||
|
icon: isWaiting ? WAITING_ICON : NORMAL_ICON
|
||||||
|
// No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var hasEventBridge = false;
|
||||||
|
function wireEventBridge(on) {
|
||||||
|
if (on) {
|
||||||
|
if (!hasEventBridge) {
|
||||||
|
tablet.fromQml.connect(fromQml);
|
||||||
|
hasEventBridge = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasEventBridge) {
|
||||||
|
tablet.fromQml.disconnect(fromQml);
|
||||||
|
hasEventBridge = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
if (onGotoScreen) {
|
||||||
|
// for toolbar-mode: go back to home screen, this will close the window.
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
} else {
|
||||||
|
shouldActivateButton = true;
|
||||||
|
tablet.loadQMLSource(gotoQmlSource);
|
||||||
|
onGotoScreen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScreenChanged(type, url) {
|
||||||
|
ignore(type);
|
||||||
|
if (url === gotoQmlSource) {
|
||||||
|
onGotoScreen = true;
|
||||||
|
shouldActivateButton = true;
|
||||||
|
button.editProperties({isActive: shouldActivateButton});
|
||||||
|
wireEventBridge(true);
|
||||||
|
messagesWaiting(false);
|
||||||
|
} else {
|
||||||
|
shouldActivateButton = false;
|
||||||
|
onGotoScreen = false;
|
||||||
|
button.editProperties({isActive: shouldActivateButton});
|
||||||
|
wireEventBridge(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
|
||||||
var stories = {};
|
var stories = {};
|
||||||
var DEBUG = false;
|
var DEBUG = false;
|
||||||
|
|
Loading…
Reference in a new issue