From 43c29500bf8483a5c974f03053fa77e43a3f142b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 3 Oct 2016 12:03:23 -0700 Subject: [PATCH 1/2] ac script to provide an on-demand crowd, and an interface script to summon it --- .../tests/performance/crowd-agent.js | 95 +++++++++++++++++++ scripts/developer/tests/performance/summon.js | 94 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 scripts/developer/tests/performance/crowd-agent.js create mode 100644 scripts/developer/tests/performance/summon.js diff --git a/scripts/developer/tests/performance/crowd-agent.js b/scripts/developer/tests/performance/crowd-agent.js new file mode 100644 index 0000000000..5df576cf99 --- /dev/null +++ b/scripts/developer/tests/performance/crowd-agent.js @@ -0,0 +1,95 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/ +// +// crowd-agent.js +// scripts/developer/tests/performance/ +// +// Created by Howard Stearns on 9/29/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Add this to domain-settings scripts url with n instances. It will lie dormant until +// a script like summon.js calls up to n avatars to be around you. + +var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd"; + +print('crowd-agent version 1'); + +/* Observations: +- File urls for AC scripts silently fail. Use a local server (e.g., python SimpleHTTPServer) for development. +- URLs are cached regardless of server headers. Must use cache-defeating query parameters. +- JSON.stringify(Avatar) silently fails (even when Agent.isAvatar) +*/ + +function startAgent(parameters) { // Can also be used to update. + print('crowd-agent starting params', JSON.stringify(parameters), JSON.stringify(Agent)); + Agent.isAvatar = true; + if (parameters.position) { + Avatar.position = parameters.position; + } + if (parameters.orientation) { + Avatar.orientation = parameters.orientation; + } + if (parameters.skeletonModelURL) { + Avatar.skeletonModelURL = parameters.skeletonModelURL; + } + if (parameters.animationData) { + data = parameters.animationData; + Avatar.startAnimation(data.url, data.fps || 30, 1.0, (data.loopFlag === undefined) ? true : data.loopFlag, false, data.startFrame || 0, data.endFrame); + } + print('crowd-agent avatars started'); +} +function stopAgent(parameters) { + Agent.isAvatar = false; + print('crowd-agent stopped', JSON.stringify(parameters), JSON.stringify(Agent)); +} + +function messageSend(message) { + Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); +} +function messageHandler(channel, messageString, senderID) { + if (channel !== MESSAGE_CHANNEL) { + return; + } + print('crowd-agent message', channel, messageString, senderID); + if (Agent.sessionUUID === senderID) { // ignore my own + return; + } + var message = {}; + try { + message = JSON.parse(messageString); + } catch (e) { + print(e); + } + switch (message.key) { + case "HELO": + messageSend({key: 'hello'}); // Allow the coordinator to count responses and make assignments. + break; + case 'hello': // ignore responses (e.g., from other agents) + break; + case "SUMMON": + if (message.rcpt === Agent.sessionUUID) { + startAgent(message); + } + break; + case "STOP": + if (message.rcpt === Agent.sessionUUID) { + stopAgent(message); + } + break; + default: + print("crowd-agent received unrecognized message:", channel, messageString, senderID); + } +} +Messages.subscribe(MESSAGE_CHANNEL); +Messages.messageReceived.connect(messageHandler); + +Script.scriptEnding.connect(function () { + print('crowd-agent shutting down'); + Messages.messageReceived.disconnect(messageHandler); + Messages.unsubscribe(MESSAGE_CHANNEL); + print('crowd-agent unsubscribed'); +}); diff --git a/scripts/developer/tests/performance/summon.js b/scripts/developer/tests/performance/summon.js new file mode 100644 index 0000000000..cb3093ca5b --- /dev/null +++ b/scripts/developer/tests/performance/summon.js @@ -0,0 +1,94 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/ +// +// crowd-agent.js +// scripts/developer/tests/performance/ +// +// Created by Howard Stearns on 9/29/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// See crowd-agent.js + +var version = 1; +var label = "summon"; +function debug() { + print.apply(null, [].concat.apply([label, version], [].map.call(arguments, JSON.stringify))); +} +var MINIMUM_AVATARS = 25; +var DENSITY = 0.3; // square meters per person. Some say 10 sq ft is arm's length (0.9m^2), 4.5 is crowd (0.4m^2), 2.5 is mosh pit (0.2m^2). +var spread = Math.sqrt(MINIMUM_AVATARS * DENSITY); // meters +var turnSpread = 90; // How many degrees should turn from front range over. + +function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2. + + +var summonedAgents = []; +var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd"; +function messageSend(message) { + Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); +} +function messageHandler(channel, messageString, senderID) { + if (channel !== MESSAGE_CHANNEL) { + return; + } + debug('message', channel, messageString, senderID); + if (MyAvatar.sessionUUID === senderID) { // ignore my own + return; + } + var message = {}, avatarIdentifiers; + try { + message = JSON.parse(messageString); + } catch (e) { + print(e); + } + switch (message.key) { + case "hello": + // There can be avatars we've summoned that do not yet appear in the AvatarList. + avatarIdentifiers = AvatarList.getAvatarIdentifiers().filter(function (id) { return summonedAgents.indexOf(id) === -1; }); + debug('present', avatarIdentifiers, summonedAgents); + if ((summonedAgents.length + avatarIdentifiers.length) < MINIMUM_AVATARS ) { + summonedAgents.push(senderID); + messageSend({ + key: 'SUMMON', + rcpt: senderID, + position: Vec3.sum(MyAvatar.position, {x: coord(), y: 0, z: coord()}), + orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0)/*, + // No need to specify skeletonModelURL + //skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/being_of_light/being_of_light.fbx", + //skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/defaultAvatar_full.fst"/, + animationData: { // T-pose until we get animations working again. + "url": "file:///C:/Program Files/High Fidelity Release/resources/avatar/animations/idle.fbx", + //"url": "file:///c:/Program Files/High Fidelity Release/resources/avatar/animations/walk_fwd.fbx", + "startFrame": 0.0, + "endFrame": 300.0, + "timeScale": 1.0, + "loopFlag": true + }*/ + }); + } + break; + case "HELO": + Window.alert("Someone else is summoning avatars."); + break; + default: + print("crowd-agent received unrecognized message:", messageString); + } +} +Messages.subscribe(MESSAGE_CHANNEL); +Messages.messageReceived.connect(messageHandler); +Script.scriptEnding.connect(function () { + debug('stopping agents', summonedAgents); + summonedAgents.forEach(function (id) { messageSend({key: 'STOP', rcpt: id}); }); + debug('agents stopped'); + Script.setTimeout(function () { + Messages.messageReceived.disconnect(messageHandler); + Messages.unsubscribe(MESSAGE_CHANNEL); + debug('unsubscribed'); + }, 500); +}); + +messageSend({key: 'HELO'}); // Ask agents to report in now, before we start the tribbles. From cdd658d4dd842b0a708e6880f56544c6ee220332 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 4 Oct 2016 11:31:49 -0700 Subject: [PATCH 2/2] report results --- scripts/developer/tests/performance/summon.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/developer/tests/performance/summon.js b/scripts/developer/tests/performance/summon.js index cb3093ca5b..2eb1fbe301 100644 --- a/scripts/developer/tests/performance/summon.js +++ b/scripts/developer/tests/performance/summon.js @@ -91,4 +91,13 @@ Script.scriptEnding.connect(function () { }, 500); }); -messageSend({key: 'HELO'}); // Ask agents to report in now, before we start the tribbles. +messageSend({key: 'HELO'}); // Ask agents to report in now. +Script.setTimeout(function () { + if (0 === summonedAgents.length) { + Window.alert("No agents reported.\n\Please run " + MINIMUM_AVATARS + " instances of\n\ +http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/crowd-agent.js\n\ +on your domain server."); + } else if (summonedAgents.length < MINIMUM_AVATARS) { + Window.alert("Only " + summonedAgents.length + " of the expected " + MINIMUM_AVATARS + " agents reported in."); + } +}, 5000);