mirror of
https://github.com/overte-org/overte.git
synced 2025-04-11 06:06:53 +02:00
Merge pull request #10333 from howard-stearns/handshake-cleanup-2
Handshake cleanup
This commit is contained in:
commit
0146d7550e
1 changed files with 278 additions and 296 deletions
|
@ -1,4 +1,6 @@
|
|||
"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
|
||||
// scripts/system
|
||||
|
@ -9,7 +11,7 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var LABEL = "makeUserConnection";
|
||||
var MAX_AVATAR_DISTANCE = 0.2; // m
|
||||
|
@ -22,12 +24,13 @@
|
|||
MAKING_CONNECTION: 3
|
||||
};
|
||||
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 CONNECTING_INTERVAL = 100; // ms
|
||||
var MAKING_CONNECTION_TIMEOUT = 800; // ms
|
||||
var CONNECTING_TIME = 1600; // ms
|
||||
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 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', ''];
|
||||
|
@ -39,7 +42,7 @@
|
|||
var PARTICLE_EFFECT_PROPS = {
|
||||
"alpha": 0.8,
|
||||
"azimuthFinish": Math.PI,
|
||||
"azimuthStart": -1*Math.PI,
|
||||
"azimuthStart": -1 * Math.PI,
|
||||
"emitRate": 500,
|
||||
"emitSpeed": 0.0,
|
||||
"emitterShouldTrail": 1,
|
||||
|
@ -56,10 +59,10 @@
|
|||
"color": {"red": 255, "green": 255, "blue": 255},
|
||||
"colorFinish": {"red": 0, "green": 164, "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},
|
||||
"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"
|
||||
};
|
||||
var MAKING_CONNECTION_PARTICLE_PROPS = {
|
||||
|
@ -68,7 +71,7 @@
|
|||
"alphaSpread": 0,
|
||||
"alphaFinish": 0,
|
||||
"azimuthFinish": Math.PI,
|
||||
"azimuthStart": -1*Math.PI,
|
||||
"azimuthStart": -1 * Math.PI,
|
||||
"emitRate": 2000,
|
||||
"emitSpeed": 0.0,
|
||||
"emitterShouldTrail": 1,
|
||||
|
@ -86,14 +89,14 @@
|
|||
"color": {"red": 200, "green": 170, "blue": 255},
|
||||
"colorFinish": {"red": 0, "green": 134, "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},
|
||||
"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"
|
||||
};
|
||||
|
||||
var currentHand = undefined;
|
||||
var currentHand;
|
||||
var currentHandJointIndex = -1;
|
||||
var state = STATES.INACTIVE;
|
||||
var connectingInterval;
|
||||
|
@ -101,7 +104,6 @@
|
|||
var makingConnectionTimeout;
|
||||
var animHandlerId;
|
||||
var connectingId;
|
||||
var connectingHandString;
|
||||
var connectingHandJointIndex = -1;
|
||||
var waitingList = {};
|
||||
var particleEffect;
|
||||
|
@ -116,7 +118,7 @@
|
|||
|
||||
function debug() {
|
||||
var stateString = "<" + STATE_STRINGS[state] + ">";
|
||||
var connecting = "[" + connectingId + "/" + connectingHandString + "]";
|
||||
var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]";
|
||||
print.apply(null, [].concat.apply([LABEL, stateString, JSON.stringify(waitingList), connecting],
|
||||
[].map.call(arguments, JSON.stringify)));
|
||||
}
|
||||
|
@ -183,27 +185,19 @@
|
|||
function handToString(hand) {
|
||||
if (hand === Controller.Standard.RightHand) {
|
||||
return "RightHand";
|
||||
} else if (hand === Controller.Standard.LeftHand) {
|
||||
}
|
||||
if (hand === Controller.Standard.LeftHand) {
|
||||
return "LeftHand";
|
||||
}
|
||||
debug("handToString called without valid hand! value: ", hand);
|
||||
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) {
|
||||
if (hand === Controller.Standard.RightHand) {
|
||||
return 1;
|
||||
} else if (hand === Controller.Standard.LeftHand) {
|
||||
}
|
||||
if (hand === Controller.Standard.LeftHand) {
|
||||
return 0;
|
||||
}
|
||||
debug("handToHaptic called without a valid hand!");
|
||||
|
@ -229,13 +223,13 @@
|
|||
}
|
||||
|
||||
// This returns the ideal hand joint index for the avatar.
|
||||
// [hand]middle1 -> [hand]index1 -> [hand]
|
||||
function getIdealHandJointIndex(avatar, hand) {
|
||||
debug("got hand " + hand + " for avatar " + avatar.sessionUUID);
|
||||
var handString = handToString(hand);
|
||||
for (var i = 0; i < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; i++) {
|
||||
var jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[i];
|
||||
var jointIndex = avatar.getJointIndex(jointName);
|
||||
// [handString]middle1 -> [handString]index1 -> [handString]
|
||||
function getIdealHandJointIndex(avatar, handString) {
|
||||
debug("get hand " + handString + " for avatar " + (avatar && avatar.sessionUUID));
|
||||
var suffixIndex, jointName, jointIndex;
|
||||
for (suffixIndex = 0; suffixIndex < (avatar ? PREFERRER_HAND_JOINT_POSTFIX_ORDER.length : 0); suffixIndex++) {
|
||||
jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex];
|
||||
jointIndex = avatar.getJointIndex(jointName);
|
||||
if (jointIndex !== -1) {
|
||||
debug('found joint ' + jointName + ' (' + jointIndex + ')');
|
||||
return jointIndex;
|
||||
|
@ -249,26 +243,39 @@
|
|||
function getHandPosition(avatar, handJointIndex) {
|
||||
if (handJointIndex === -1) {
|
||||
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.getJointPosition(handJointIndex);
|
||||
}
|
||||
|
||||
function shakeHandsAnimation(animationProperties) {
|
||||
var animationData = {};
|
||||
function updateAnimationData() {
|
||||
// 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
|
||||
// far in front as scaling with the avatar's height (say hips
|
||||
// to head distance)
|
||||
var headIndex = MyAvatar.getJointIndex("Head");
|
||||
var offset = 0.5; // default distance of hand in front of you
|
||||
var result = {};
|
||||
if (headIndex) {
|
||||
offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
|
||||
}
|
||||
result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
|
||||
result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
|
||||
return result;
|
||||
animationData.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
|
||||
animationData.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
|
||||
}
|
||||
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) {
|
||||
|
@ -294,11 +301,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
function calcParticlePos(myHand, otherHand, otherOrientation, reset) {
|
||||
function calcParticlePos(myHandPosition, otherHandPosition, otherOrientation, reset) {
|
||||
if (reset) {
|
||||
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
|
||||
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);
|
||||
|
@ -314,80 +321,78 @@
|
|||
}
|
||||
|
||||
var myHandPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||
var otherHand;
|
||||
var otherHandPosition;
|
||||
var otherOrientation;
|
||||
if (connectingId) {
|
||||
var other = AvatarList.getAvatar(connectingId);
|
||||
if (other) {
|
||||
otherOrientation = other.orientation;
|
||||
otherHand = getHandPosition(other, connectingHandJointIndex);
|
||||
otherHandPosition = getHandPosition(other, connectingHandJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case STATES.WAITING:
|
||||
// no visualization while waiting
|
||||
deleteParticleEffect();
|
||||
deleteMakeConnectionParticleEffect();
|
||||
stopHandshakeSound();
|
||||
break;
|
||||
case STATES.CONNECTING:
|
||||
var particleProps = {};
|
||||
// put the position between the 2 hands, if we have a connectingId. This
|
||||
// helps define the plane in which the particles move.
|
||||
positionFractionallyTowards(myHandPosition, otherHand, 0.5);
|
||||
// now manage the rest of the entity
|
||||
if (!particleEffect) {
|
||||
particleRotationAngle = 0.0;
|
||||
particleEmitRate = 500;
|
||||
particleProps = PARTICLE_EFFECT_PROPS;
|
||||
particleProps.isEmitting = 0;
|
||||
particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation);
|
||||
particleProps.parentID = MyAvatar.sessionUUID;
|
||||
particleEffect = Entities.addEntity(particleProps, true);
|
||||
} else {
|
||||
particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation);
|
||||
particleProps.isEmitting = 1;
|
||||
Entities.editEntity(particleEffect, particleProps);
|
||||
}
|
||||
if (!makingConnectionParticleEffect) {
|
||||
var props = MAKING_CONNECTION_PARTICLE_PROPS;
|
||||
props.parentID = MyAvatar.sessionUUID;
|
||||
makingConnectionEmitRate = 2000;
|
||||
props.emitRate = makingConnectionEmitRate;
|
||||
props.position = myHandPosition;
|
||||
makingConnectionParticleEffect = Entities.addEntity(props, true);
|
||||
} else {
|
||||
makingConnectionEmitRate *= 0.5;
|
||||
Entities.editEntity(makingConnectionParticleEffect, {
|
||||
emitRate: makingConnectionEmitRate,
|
||||
position: myHandPosition,
|
||||
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
|
||||
case STATES.WAITING:
|
||||
// no visualization while waiting
|
||||
deleteParticleEffect();
|
||||
deleteMakeConnectionParticleEffect();
|
||||
stopHandshakeSound();
|
||||
break;
|
||||
case STATES.CONNECTING:
|
||||
var particleProps = {};
|
||||
// put the position between the 2 hands, if we have a connectingId. This
|
||||
// helps define the plane in which the particles move.
|
||||
positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5);
|
||||
// now manage the rest of the entity
|
||||
if (!particleEffect) {
|
||||
particleRotationAngle = 0.0;
|
||||
particleEmitRate = 500;
|
||||
particleProps = PARTICLE_EFFECT_PROPS;
|
||||
particleProps.isEmitting = 0;
|
||||
particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation);
|
||||
particleProps.parentID = MyAvatar.sessionUUID;
|
||||
particleEffect = Entities.addEntity(particleProps, true);
|
||||
} else {
|
||||
particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation);
|
||||
particleProps.isEmitting = 1;
|
||||
Entities.editEntity(particleEffect, particleProps);
|
||||
}
|
||||
if (!makingConnectionParticleEffect) {
|
||||
var props = MAKING_CONNECTION_PARTICLE_PROPS;
|
||||
props.parentID = MyAvatar.sessionUUID;
|
||||
makingConnectionEmitRate = 2000;
|
||||
props.emitRate = makingConnectionEmitRate;
|
||||
props.position = myHandPosition;
|
||||
makingConnectionParticleEffect = Entities.addEntity(props, true);
|
||||
} else {
|
||||
makingConnectionEmitRate *= 0.5;
|
||||
Entities.editEntity(makingConnectionParticleEffect, {
|
||||
emitRate: makingConnectionEmitRate,
|
||||
position: myHandPosition,
|
||||
isEmitting: true
|
||||
});
|
||||
break;
|
||||
default:
|
||||
debug("unexpected state", state);
|
||||
break;
|
||||
}
|
||||
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, otherHandPosition, otherOrientation),
|
||||
emitRate: particleEmitRate
|
||||
});
|
||||
break;
|
||||
default:
|
||||
debug("unexpected state", state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function isNearby(id, hand) {
|
||||
function isNearby() {
|
||||
if (currentHand) {
|
||||
var handPos = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||
var avatar = AvatarList.getAvatar(id);
|
||||
var handPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||
var avatar = AvatarList.getAvatar(connectingId);
|
||||
if (avatar) {
|
||||
var otherHand = stringToHand(hand);
|
||||
var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand);
|
||||
var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPos);
|
||||
var distance = Vec3.distance(getHandPosition(avatar, connectingHandJointIndex), handPosition);
|
||||
return (distance < MAX_AVATAR_DISTANCE);
|
||||
}
|
||||
}
|
||||
|
@ -395,68 +400,90 @@
|
|||
}
|
||||
|
||||
function findNearestWaitingAvatar() {
|
||||
var handPos = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||
var handPosition = getHandPosition(MyAvatar, currentHandJointIndex);
|
||||
var minDistance = MAX_AVATAR_DISTANCE;
|
||||
var nearestAvatar = {};
|
||||
Object.keys(waitingList).forEach(function (identifier) {
|
||||
var avatar = AvatarList.getAvatar(identifier);
|
||||
if (avatar) {
|
||||
var hand = stringToHand(waitingList[identifier]);
|
||||
var handJointIndex = getIdealHandJointIndex(avatar, hand);
|
||||
var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPos);
|
||||
var handJointIndex = waitingList[identifier];
|
||||
var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar};
|
||||
nearestAvatar = {avatarId: identifier, jointIndex: handJointIndex};
|
||||
}
|
||||
}
|
||||
});
|
||||
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
|
||||
// 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
|
||||
// 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) {
|
||||
if (fromKeyboard) {
|
||||
debug("adding animation");
|
||||
// just in case order of press/unpress is broken
|
||||
if (animHandlerId) {
|
||||
animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId);
|
||||
}
|
||||
animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []);
|
||||
startHandshakeAnimation();
|
||||
}
|
||||
debug("starting handshake for", currentHand);
|
||||
pollCount = 0;
|
||||
state = STATES.WAITING;
|
||||
connectingId = undefined;
|
||||
connectingHandString = undefined;
|
||||
connectingHandJointIndex = -1;
|
||||
clearConnecting();
|
||||
// just in case
|
||||
stopWaiting();
|
||||
stopConnecting();
|
||||
stopMakingConnection();
|
||||
|
||||
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 {
|
||||
if (!setupCandidate()) {
|
||||
// send waiting message
|
||||
debug("sending waiting message");
|
||||
messageSend({
|
||||
handStringMessageSend({
|
||||
key: "waiting",
|
||||
hand: handToString(currentHand)
|
||||
});
|
||||
lookForWaitingAvatar();
|
||||
}
|
||||
|
@ -474,9 +501,7 @@
|
|||
// as we ignore the key release event when inactive. See updateTriggers
|
||||
// below.
|
||||
state = STATES.INACTIVE;
|
||||
connectingId = undefined;
|
||||
connectingHandString = undefined;
|
||||
connectingHandJointIndex = -1;
|
||||
clearConnecting();
|
||||
stopWaiting();
|
||||
stopConnecting();
|
||||
stopMakingConnection();
|
||||
|
@ -486,10 +511,7 @@
|
|||
key: "done"
|
||||
});
|
||||
|
||||
if (animHandlerId) {
|
||||
debug("removing animation");
|
||||
MyAvatar.removeAnimationStateHandler(animHandlerId);
|
||||
}
|
||||
endHandshakeAnimation();
|
||||
// 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);
|
||||
|
@ -500,19 +522,16 @@
|
|||
debug("currentHand", currentHand, "ignoring messages from", hand);
|
||||
return;
|
||||
}
|
||||
if (!currentHand) {
|
||||
currentHand = hand;
|
||||
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand);
|
||||
}
|
||||
currentHand = hand;
|
||||
currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton.
|
||||
// ok now, we are either initiating or quitting...
|
||||
var isGripping = value > GRIP_MIN;
|
||||
if (isGripping) {
|
||||
debug("updateTriggers called - gripping", handToString(hand));
|
||||
if (state !== STATES.INACTIVE) {
|
||||
return;
|
||||
} else {
|
||||
startHandshake(fromKeyboard);
|
||||
}
|
||||
startHandshake(fromKeyboard);
|
||||
} else {
|
||||
// TODO: should we end handshake even when inactive? Ponder
|
||||
debug("updateTriggers called -- no longer gripping", handToString(hand));
|
||||
|
@ -524,47 +543,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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';
|
||||
var result, requestBody;
|
||||
function connectionRequestCompleted() { // Final result is in. Do effects.
|
||||
if (result.status === 'success') { // set earlier
|
||||
if (!successfulHandshakeInjector) {
|
||||
|
@ -580,16 +564,37 @@
|
|||
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);
|
||||
UserActivityLogger.makeUserConnection(connectingId, true, result.connection.new_connection ?
|
||||
"new connection" : "already connected");
|
||||
Window.makeConnection(true,
|
||||
result.connection.new_connection ?
|
||||
"You and " + result.connection.username + " are now connected!" :
|
||||
result.connection.username);
|
||||
UserActivityLogger.makeUserConnection(connectingId,
|
||||
true,
|
||||
result.connection.new_connection ?
|
||||
"new connection" :
|
||||
"already connected");
|
||||
return;
|
||||
} // failed
|
||||
endHandshake();
|
||||
debug("failing with result data", result);
|
||||
// IWBNI we also did some fail sound/visual effect.
|
||||
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);
|
||||
}
|
||||
// This is a bit fragile - but to account for skew in when people actually create the
|
||||
|
@ -606,7 +611,7 @@
|
|||
debug(response, 'pollCount', pollCount);
|
||||
if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long.
|
||||
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();
|
||||
} else { // poll
|
||||
Script.setTimeout(function () {
|
||||
|
@ -636,8 +641,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// this should be where we make the appropriate connection call. For now just make the
|
||||
// visualization change.
|
||||
function makeConnection(id) {
|
||||
// send done to let the connection know you have made connection.
|
||||
messageSend({
|
||||
|
@ -647,8 +650,7 @@
|
|||
|
||||
state = STATES.MAKING_CONNECTION;
|
||||
|
||||
// continue the haptic background until the timeout fires. When we make calls, we will have an interval
|
||||
// probably, in which we do this.
|
||||
// continue the haptic background until the timeout fires.
|
||||
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
|
||||
|
||||
|
@ -662,26 +664,27 @@
|
|||
|
||||
// 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).
|
||||
request({ //
|
||||
request({
|
||||
uri: requestUrl,
|
||||
method: 'POST',
|
||||
json: true,
|
||||
body: {'user_connection_request': requestBody}
|
||||
}, handleConnectionResponseAndMaybeRepeat);
|
||||
}
|
||||
function setupConnecting(id, jointIndex) {
|
||||
connectingId = id;
|
||||
connectingHandJointIndex = jointIndex;
|
||||
}
|
||||
|
||||
// we change states, start the connectionInterval where we check
|
||||
// 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 entire CONNECTING_TIME, we make the connection.
|
||||
function startConnecting(id, hand) {
|
||||
function startConnecting(id, jointIndex) {
|
||||
var count = 0;
|
||||
debug("connecting", id, "hand", hand);
|
||||
debug("connecting", id, "hand", jointIndex);
|
||||
// do we need to do this?
|
||||
connectingId = id;
|
||||
connectingHandString = hand;
|
||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
||||
setupConnecting(id, jointIndex);
|
||||
state = STATES.CONNECTING;
|
||||
|
||||
// play sound
|
||||
|
@ -696,10 +699,9 @@
|
|||
}
|
||||
|
||||
// send message that we are connecting with them
|
||||
messageSend({
|
||||
handStringMessageSend({
|
||||
key: "connecting",
|
||||
id: id,
|
||||
hand: handToString(currentHand)
|
||||
id: id
|
||||
});
|
||||
Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand));
|
||||
|
||||
|
@ -710,7 +712,7 @@
|
|||
if (state !== STATES.CONNECTING) {
|
||||
debug("stopping connecting interval, state changed");
|
||||
stopConnecting();
|
||||
} else if (!isNearby(id, hand)) {
|
||||
} else if (!isNearby()) {
|
||||
// gotta go back to waiting
|
||||
debug(id, "moved, back to waiting");
|
||||
stopConnecting();
|
||||
|
@ -718,7 +720,7 @@
|
|||
key: "done"
|
||||
});
|
||||
startHandshake();
|
||||
} else if (count > CONNECTING_TIME/CONNECTING_INTERVAL) {
|
||||
} else if (count > CONNECTING_TIME / CONNECTING_INTERVAL) {
|
||||
debug("made connection with " + id);
|
||||
makeConnection(id);
|
||||
stopConnecting();
|
||||
|
@ -744,140 +746,120 @@
|
|||
| ---------- (done) ---------> |
|
||||
*/
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
||||
return;
|
||||
}
|
||||
var message = {};
|
||||
try {
|
||||
message = JSON.parse(messageString);
|
||||
} catch (e) {
|
||||
debug(e);
|
||||
}
|
||||
switch (message.key) {
|
||||
case "waiting":
|
||||
// add this guy to waiting object. Any other message from this person will
|
||||
// remove it from the list
|
||||
waitingList[senderID] = message.hand;
|
||||
break;
|
||||
case "connectionRequest":
|
||||
delete waitingList[senderID];
|
||||
if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID &&
|
||||
(!connectingId || connectingId === senderID)) {
|
||||
// you were waiting for a connection request, so send the ack. Or, you and the other
|
||||
// guy raced and both send connectionRequests. Handle that too
|
||||
connectingId = senderID;
|
||||
connectingHandString = message.hand;
|
||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
||||
messageSend({
|
||||
key: "connectionAck",
|
||||
id: senderID,
|
||||
hand: handToString(currentHand)
|
||||
});
|
||||
} else if (state === STATES.WAITING && connectingId === senderID) {
|
||||
// the person you are trying to connect sent a request to someone else. See the
|
||||
// if statement above. So, don't cry, just start the handshake over again
|
||||
case "waiting":
|
||||
// add this guy to waiting object. Any other message from this person will remove it from the list
|
||||
waitingList[senderID] = getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]);
|
||||
break;
|
||||
case "connectionRequest":
|
||||
delete waitingList[senderID];
|
||||
if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) {
|
||||
// you were waiting for a connection request, so send the ack. Or, you and the other
|
||||
// guy raced and both send connectionRequests. Handle that too
|
||||
setupConnecting(senderID, exisitingOrSearchedJointIndex());
|
||||
handStringMessageSend({
|
||||
key: "connectionAck",
|
||||
id: senderID,
|
||||
});
|
||||
} else if (state === STATES.WAITING && connectingId === senderID) {
|
||||
// the person you are trying to connect sent a request to someone else. See the
|
||||
// if statement above. So, don't cry, just start the handshake over again
|
||||
startHandshake();
|
||||
}
|
||||
break;
|
||||
case "connectionAck":
|
||||
delete waitingList[senderID];
|
||||
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();
|
||||
}
|
||||
break;
|
||||
case "connectionAck":
|
||||
delete waitingList[senderID];
|
||||
if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) {
|
||||
if (message.id === MyAvatar.sessionUUID) {
|
||||
// start connecting...
|
||||
connectingId = senderID;
|
||||
connectingHandString = message.hand;
|
||||
connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ?
|
||||
getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1;
|
||||
stopWaiting();
|
||||
startConnecting(senderID, connectingHandString);
|
||||
} else if (connectingId) {
|
||||
// this is for someone else (we lost race in connectionRequest),
|
||||
// so lets start over
|
||||
}
|
||||
// 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;
|
||||
case "connecting":
|
||||
delete waitingList[senderID];
|
||||
if (state === STATES.WAITING && senderID === connectingId) {
|
||||
if (message.id !== MyAvatar.sessionUUID) {
|
||||
// the person we were trying to connect is connecting to someone else
|
||||
// so try again
|
||||
startHandshake();
|
||||
break;
|
||||
}
|
||||
startConnecting(senderID, connectingHandJointIndex);
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
case "connecting":
|
||||
delete waitingList[senderID];
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("unknown message", message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Messages.subscribe(MESSAGE_CHANNEL);
|
||||
Messages.messageReceived.connect(messageHandler);
|
||||
|
||||
|
||||
function makeGripHandler(hand, animate) {
|
||||
// determine if we are gripping or un-gripping
|
||||
if (animate) {
|
||||
return function(value) {
|
||||
return function (value) {
|
||||
updateTriggers(value, true, hand);
|
||||
};
|
||||
|
||||
} else {
|
||||
return function (value) {
|
||||
updateTriggers(value, false, hand);
|
||||
};
|
||||
}
|
||||
return function (value) {
|
||||
updateTriggers(value, false, hand);
|
||||
};
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl &&
|
||||
!event.isAlt) {
|
||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) {
|
||||
updateTriggers(1.0, true, Controller.Standard.RightHand);
|
||||
}
|
||||
}
|
||||
function keyReleaseEvent(event) {
|
||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl &&
|
||||
!event.isAlt) {
|
||||
if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) {
|
||||
updateTriggers(0.0, true, Controller.Standard.RightHand);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue