content/hifi-content/DomainContent/Welcome Area/Scripts/ACCleanupAndSpawnBot2.js
2022-02-13 22:49:05 +01:00

321 lines
11 KiB
JavaScript

// ACCleanupAndSpawnBot.js
//
// Created by Thijs Wenker on 9/26/2016
//
// This Assignment Client script cleans up a the defined
// 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
// Times when the cleanup script runs (on a daily basis 00:00 -> 23:59)
var RUN_WHEN = [
'0:00',
'2:00',
'4:00',
'6:00',
'8:00',
'10:00',
'12:00',
'14:00',
'16:00',
'18:00',
'20:00',
'22:00'
];
// Cleanup filters
//
// Allows the following entry-types:
// Name (string) eg: 'This is a cube'
// Name Regular Expression (RexExp) eg: /Cube[0-9]+/
// Filter Object (JS Object eg:
// {
// name: 'Cube',
// locked: false,
// visible: true
// }
var CLEANUP_FILTER = [
{
locked: 0
}
];
// The script will only run in the domain with the following placename:
// (The PLACENAME_LOCK is only effective when the script is ran in Interface, not for Assignment Client scripts)
var PLACENAME_LOCK = 'dev-welcome';
// IMPORT_ENTITIES supports JSON exported entities using importEntitiesJSON(URL, parentProperties); e.g:
// importEntitiesJSON('https://www.acme.com/example/cubes.json', {position: MyAvatar.position})
var IMPORT_ENTITIES = [
importEntitiesJSON('http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/Scripts/seesaw.json', {position: {x:55.613121032714844, y: 0.29320013523101807, z: -44.694454193115234}}),
importEntitiesJSON('http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/Scripts/domino.json', {position: {"x":33.79003143310547,"y":0.34712982177734375,"z":-103.43145751953125}}),
importEntitiesJSON('http://hifi-content.s3.amazonaws.com/DomainContent/Welcome%20Area/Scripts/castle7.json', {position: {"x":-20.238300323486328,"y":-0.2793,"z":-55.61320114135742}})
];
// You can use DRY RUN mode to test the cleanup filter and see which entities will be
// deleted without modifying anything
var DRY_RUN = false;
var SEARCH_CENTER = {x: -26.7, y: 0.1, z: -0.4};
var SEARCH_AREA = 60000; // search area (sphere) in meters radius
// Allows you to manually trigger the cleanups through the messages system
var REMOTE_TRIGGER_ENABLED = true;
var TRIGGER_CHANNEL = 'cleanUpAndSpawnBot';
var CLEAN_MESSAGE = 'cleanUp';
var SPAWN_MESSAGE = 'spawn';
var CLEAN_AND_SPAWN_MESSAGE = 'cleanUpAndSpawn';
var DEBUG_SCRIPT_OVER_CHANNEL = '';
// Constants
const HOURS_IN_DAY = 24;
const MINUTES_IN_HOUR = 60;
const SECONDS_PER_MINUTE = 60;
const MILLISECONDS_PER_SECOND = 1000;
const MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR;
const TIMER_TICKS_PER_MINUTE = SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
const ZERO_UUID = '{00000000-0000-0000-0000-000000000000}';
const IS_AC_SCRIPT = (this.Agent !== undefined);
const SANETIZE_PROPERTIES = ['childEntities', 'parentID', 'id'];
function isCorrectPlace() {
return location !== undefined && location.hostname === PLACENAME_LOCK;
}
var originalPrint = print;
function networkedDebugPrint(message) {
originalPrint(message);
Messages.sendMessage(DEBUG_SCRIPT_OVER_CHANNEL, message);
}
if (DEBUG_SCRIPT_OVER_CHANNEL !== '') {
print = networkedDebugPrint;
}
function entityListToTree(entitiesList) {
function entityListToTreeRecursive(properties) {
properties.childEntities = [];
entitiesList.forEach(function(entityProperties) {
if (properties.id === entityProperties.parentID) {
properties.childEntities.push(entityListToTreeRecursive(entityProperties));
}
});
return properties;
}
var entityTree = [];
entitiesList.forEach(function(entityProperties) {
if (entityProperties.parentID === undefined || entityProperties.parentID === ZERO_UUID) {
entityTree.push(entityListToTreeRecursive(entityProperties));
}
});
return entityTree;
}
// TODO: ATP support (currently the JS API for ATP does not support file links, only hashes)
function importEntitiesJSON(importLink, parentProperties) {
if (parentProperties === undefined) {
parentProperties = {};
}
var request = new XMLHttpRequest();
request.open('GET', importLink, false);
request.send();
try {
var response = JSON.parse(request.responseText);
parentProperties.childEntities = entityListToTree(response.Entities);
return parentProperties;
} catch (e) {
print('Failed importing entities JSON because: ' + JSON.stringify(e));
}
return null;
}
//Creates an entity and returns a mixed object of the creation properties and the assigned entityID
var createEntity = function(entityProperties, parent) {
if (parent.rotation !== undefined) {
if (entityProperties.rotation !== undefined) {
entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation);
} else {
entityProperties.rotation = parent.rotation;
}
}
if (parent.position !== undefined) {
var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position;
entityProperties.position = Vec3.sum(localPosition, parent.position)
}
if (parent.id !== undefined) {
entityProperties.parentID = parent.id;
}
entityProperties.id = Entities.addEntity(entityProperties);
return entityProperties;
};
var createEntitiesFromTree = function(entityTree, parent) {
if (parent === undefined) {
parent = {};
}
var createdTree = [];
entityTree.forEach(function(entityProperties) {
var sanetizedProperties = {};
Object.keys(entityProperties).forEach(function(propertyKey) {
if (!entityProperties.hasOwnProperty(propertyKey) || SANETIZE_PROPERTIES.indexOf(propertyKey) !== -1) {
return true;
}
sanetizedProperties[propertyKey] = entityProperties[propertyKey];
});
// Allow for non-entity parent objects, this allows us to offset groups of entities to a specific position/rotation
var parentProperties = sanetizedProperties;
if (entityProperties.type !== undefined) {
parentProperties = createEntity(sanetizedProperties, parent);
}
if (entityProperties.childEntities !== undefined) {
parentProperties.childEntities =
createEntitiesFromTree(entityProperties.childEntities, parentProperties);
}
createdTree.push(parentProperties);
});
return createdTree;
};
function getTimeUntilNextRun(times) {
var parsedTimes = [];
var now = new Date();
var currentMinuteInDay = (MINUTES_IN_HOUR * now.getHours()) + now.getMinutes();
times.forEach(function(timeText) {
try {
var timeArray = timeText.split(':');
var hour = parseInt(timeArray[0]);
var minute = parseInt(timeArray[1]);
var minuteInDay = (MINUTES_IN_HOUR * hour) + minute;
var minutesToNextRun = minuteInDay - currentMinuteInDay + (minuteInDay <= currentMinuteInDay ? MINUTES_IN_DAY : 0);
parsedTimes.push(TIMER_TICKS_PER_MINUTE * minutesToNextRun);
} catch (e) {
print('Had some trouble while parsing the following time: ' + timeText);
}
});
return Math.min.apply(Math, parsedTimes);
}
function triggerEventOn(times, callback) {
var timeout = getTimeUntilNextRun(times);
print('Set timeout to ' + timeout + ' ms.');
Script.setTimeout(callback, timeout);
}
var doCleanup = function() {
print('Doing Cleanup and Spawn.');
purgeEntities();
importEntities();
triggerEventOn(RUN_WHEN, doCleanup);
};
var testFilter = function(filter, properties) {
var testFilter = typeof(filter) === 'object' ? filter : {name: filter};
for (key in testFilter) {
if (!testFilter.hasOwnProperty(key)) {
continue;
}
if (testFilter[key] !== properties[key]) {
return false;
}
}
return true;
};
var purgeEntities = function() {
if (!IS_AC_SCRIPT && !isCorrectPlace()) {
print('The PLACENAME_LOCK does not match the current placename, aborting purgeEntities.')
return;
}
var propertiesToRequest = ['name'];
CLEANUP_FILTER.forEach(function(filter) {
if (typeof(filter) === 'object') {
Object.keys(filter).forEach(function(property) {
if (propertiesToRequest.indexOf(property) === -1) {
propertiesToRequest.push(property);
}
});
}
});
Entities.findEntities(SEARCH_CENTER, SEARCH_AREA).forEach(function(entityID) {
var entityProperties = Entities.getEntityProperties(entityID, propertiesToRequest);
var deleteEntity = false;
CLEANUP_FILTER.forEach(function(filter) {
if (testFilter(filter, entityProperties)) {
deleteEntity = true;
return true;
}
});
if (deleteEntity) {
print(DRY_RUN ? '[DRY_RUN] Would have removed: ' : 'removing: ' + entityProperties.id + ' ' + entityProperties.name);
if (!DRY_RUN) {
Entities.deleteEntity(entityProperties.id);
}
}
});
};
var importEntities = function() {
if (DRY_RUN) {
print('Skipping importEntities for the DRY_RUN.');
return {};
}
if (!IS_AC_SCRIPT && !isCorrectPlace()) {
print('The PLACENAME_LOCK does not match the current placename, aborting importEntities.')
return {};
}
return createEntitiesFromTree(IMPORT_ENTITIES);
};
triggerEventOn(RUN_WHEN, doCleanup);
if (REMOTE_TRIGGER_ENABLED) {
Messages.messageReceived.connect(function(channel, message, senderUUID, localOnly) {
if (channel !== TRIGGER_CHANNEL) {
return;
}
if (message === CLEAN_MESSAGE || message === CLEAN_AND_SPAWN_MESSAGE) {
purgeEntities();
}
if (message === SPAWN_MESSAGE || message === CLEAN_AND_SPAWN_MESSAGE) {
importEntities();
}
});
Messages.subscribe(TRIGGER_CHANNEL);
}
// Assignment Client related code:
if (IS_AC_SCRIPT) {
Agent.isAvatar = true;
Avatar.skeletonModelURL = 'http://hifi-content.s3.amazonaws.com/ozan/dev/avatars/invisible_avatar/invisible_avatar.fst';
var initialized = false;
function update(deltaTime) {
if (!initialized) {
if (Entities.serversExist() && Entities.canRez()) {
Entities.setPacketsPerSecond(60000);
EntityViewer.setPosition(SEARCH_CENTER);
EntityViewer.setCenterRadius(SEARCH_AREA);
Script.setInterval(function() {
EntityViewer.queryOctree();
}, 1000);
initialized = true;
Script.update.disconnect(update);
}
return;
}
}
Script.update.connect(update);
}