diff --git a/dist/networked-aframe.js b/dist/networked-aframe.js index f88b1252..4b4e6b06 100644 --- a/dist/networked-aframe.js +++ b/dist/networked-aframe.js @@ -130,7 +130,7 @@ eval("// Patched version of fast-deep-equal which does not\n// allocate memory v /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nvar options = __webpack_require__(/*! ./options */ \"./src/options.js\");\n\nvar utils = __webpack_require__(/*! ./utils */ \"./src/utils.js\");\n\nvar NafLogger = __webpack_require__(/*! ./NafLogger */ \"./src/NafLogger.js\");\n\nvar Schemas = __webpack_require__(/*! ./Schemas */ \"./src/Schemas.js\");\n\nvar NetworkEntities = __webpack_require__(/*! ./NetworkEntities */ \"./src/NetworkEntities.js\");\n\nvar NetworkConnection = __webpack_require__(/*! ./NetworkConnection */ \"./src/NetworkConnection.js\");\n\nvar AdapterFactory = __webpack_require__(/*! ./adapters/AdapterFactory */ \"./src/adapters/AdapterFactory.js\");\n\nvar naf = {};\nnaf.app = '';\nnaf.room = '';\nnaf.clientId = '';\nnaf.options = options;\nnaf.utils = utils;\nnaf.log = new NafLogger();\nnaf.schemas = new Schemas();\nnaf.version = \"0.8.2\";\nnaf.adapters = new AdapterFactory();\nvar entities = new NetworkEntities();\nvar connection = new NetworkConnection(entities);\nnaf.connection = connection;\nnaf.entities = entities;\nmodule.exports = window.NAF = naf;\n\n//# sourceURL=webpack:///./src/NafIndex.js?"); +eval("\n\nvar options = __webpack_require__(/*! ./options */ \"./src/options.js\");\n\nvar utils = __webpack_require__(/*! ./utils */ \"./src/utils.js\");\n\nvar NafLogger = __webpack_require__(/*! ./NafLogger */ \"./src/NafLogger.js\");\n\nvar Schemas = __webpack_require__(/*! ./Schemas */ \"./src/Schemas.js\");\n\nvar NetworkEntities = __webpack_require__(/*! ./NetworkEntities */ \"./src/NetworkEntities.js\");\n\nvar NetworkConnection = __webpack_require__(/*! ./NetworkConnection */ \"./src/NetworkConnection.js\");\n\nvar AdapterFactory = __webpack_require__(/*! ./adapters/AdapterFactory */ \"./src/adapters/AdapterFactory.js\");\n\nvar naf = {};\nnaf.app = '';\nnaf.room = '';\nnaf.clientId = '';\nnaf.options = options;\nnaf.utils = utils;\nnaf.log = new NafLogger();\nnaf.schemas = new Schemas();\nnaf.version = \"0.8.3\";\nnaf.adapters = new AdapterFactory();\nvar entities = new NetworkEntities();\nvar connection = new NetworkConnection(entities);\nnaf.connection = connection;\nnaf.entities = entities;\nmodule.exports = window.NAF = naf;\n\n//# sourceURL=webpack:///./src/NafIndex.js?"); /***/ }), @@ -274,7 +274,7 @@ eval("\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance insta /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n/* global AFRAME, NAF, THREE */\nvar naf = __webpack_require__(/*! ../NafIndex */ \"./src/NafIndex.js\");\n\nAFRAME.registerComponent('networked-audio-source', {\n schema: {\n positional: {\n \"default\": true\n },\n distanceModel: {\n \"default\": \"inverse\",\n oneOf: [\"linear\", \"inverse\", \"exponential\"]\n },\n maxDistance: {\n \"default\": 10000\n },\n refDistance: {\n \"default\": 1\n },\n rolloffFactor: {\n \"default\": 1\n }\n },\n init: function init() {\n var _this = this;\n\n this.listener = null;\n this.stream = null;\n this._setMediaStream = this._setMediaStream.bind(this);\n NAF.utils.getNetworkedEntity(this.el).then(function (networkedEl) {\n var ownerId = networkedEl.components.networked.data.owner;\n\n if (ownerId) {\n NAF.connection.adapter.getMediaStream(ownerId).then(_this._setMediaStream)[\"catch\"](function (e) {\n return naf.log.error(\"Error getting media stream for \".concat(ownerId), e);\n });\n } else {// Correctly configured local entity, perhaps do something here for enabling debug audio loopback\n }\n });\n },\n update: function update() {\n this._setPannerProperties();\n },\n _setMediaStream: function _setMediaStream(newStream) {\n if (!this.sound) {\n this.setupSound();\n }\n\n if (newStream != this.stream) {\n if (this.stream) {\n this.sound.disconnect();\n }\n\n if (newStream) {\n // Chrome seems to require a MediaStream be attached to an AudioElement before AudioNodes work correctly\n // We don't want to do this in other browsers, particularly in Safari, which actually plays the audio despite\n // setting the volume to 0.\n if (/chrome/i.test(navigator.userAgent)) {\n this.audioEl = new Audio();\n this.audioEl.setAttribute(\"autoplay\", \"autoplay\");\n this.audioEl.setAttribute(\"playsinline\", \"playsinline\");\n this.audioEl.srcObject = newStream;\n this.audioEl.volume = 0; // we don't actually want to hear audio from this element\n }\n\n var soundSource = this.sound.context.createMediaStreamSource(newStream);\n this.sound.setNodeSource(soundSource);\n this.el.emit('sound-source-set', {\n soundSource: soundSource\n });\n }\n\n this.stream = newStream;\n }\n },\n _setPannerProperties: function _setPannerProperties() {\n if (this.sound && this.data.positional) {\n this.sound.setDistanceModel(this.data.distanceModel);\n this.sound.setMaxDistance(this.data.maxDistance);\n this.sound.setRefDistance(this.data.refDistance);\n this.sound.setRolloffFactor(this.data.rolloffFactor);\n }\n },\n remove: function remove() {\n if (!this.sound) return;\n this.el.removeObject3D(this.attrName);\n\n if (this.stream) {\n this.sound.disconnect();\n }\n },\n setupSound: function setupSound() {\n var el = this.el;\n var sceneEl = el.sceneEl;\n\n if (this.sound) {\n el.removeObject3D(this.attrName);\n }\n\n if (!sceneEl.audioListener) {\n sceneEl.audioListener = new THREE.AudioListener();\n sceneEl.camera && sceneEl.camera.add(sceneEl.audioListener);\n sceneEl.addEventListener('camera-set-active', function (evt) {\n evt.detail.cameraEl.getObject3D('camera').add(sceneEl.audioListener);\n });\n }\n\n this.listener = sceneEl.audioListener;\n this.sound = this.data.positional ? new THREE.PositionalAudio(this.listener) : new THREE.Audio(this.listener);\n el.setObject3D(this.attrName, this.sound);\n\n this._setPannerProperties();\n }\n});\n\n//# sourceURL=webpack:///./src/components/networked-audio-source.js?"); +eval("\n\n/* global AFRAME, NAF, THREE */\nvar naf = __webpack_require__(/*! ../NafIndex */ \"./src/NafIndex.js\");\n\nAFRAME.registerComponent('networked-audio-source', {\n schema: {\n positional: {\n \"default\": true\n },\n distanceModel: {\n \"default\": \"inverse\",\n oneOf: [\"linear\", \"inverse\", \"exponential\"]\n },\n maxDistance: {\n \"default\": 10000\n },\n refDistance: {\n \"default\": 1\n },\n rolloffFactor: {\n \"default\": 1\n }\n },\n init: function init() {\n var _this = this;\n\n this.listener = null;\n this.stream = null;\n this._setMediaStream = this._setMediaStream.bind(this);\n NAF.utils.getNetworkedEntity(this.el).then(function (networkedEl) {\n var ownerId = networkedEl.components.networked.data.owner;\n\n if (ownerId) {\n NAF.connection.adapter.getMediaStream(ownerId).then(_this._setMediaStream)[\"catch\"](function (e) {\n return naf.log.error(\"Error getting media stream for \".concat(ownerId), e);\n });\n } else {// Correctly configured local entity, perhaps do something here for enabling debug audio loopback\n }\n });\n },\n update: function update() {\n this._setPannerProperties();\n },\n _setMediaStream: function _setMediaStream(newStream) {\n if (!this.sound) {\n this.setupSound();\n }\n\n if (newStream != this.stream) {\n if (this.stream) {\n this.sound.disconnect();\n }\n\n if (newStream) {\n // Chrome seems to require a MediaStream be attached to an AudioElement before AudioNodes work correctly\n // We don't want to do this in other browsers, particularly in Safari, which actually plays the audio despite\n // setting the volume to 0.\n if (/chrome/i.test(navigator.userAgent)) {\n this.audioEl = new Audio();\n this.audioEl.setAttribute(\"autoplay\", \"autoplay\");\n this.audioEl.setAttribute(\"playsinline\", \"playsinline\");\n this.audioEl.srcObject = newStream;\n this.audioEl.volume = 0; // we don't actually want to hear audio from this element\n }\n\n var soundSource = this.sound.context.createMediaStreamSource(newStream);\n this.sound.setNodeSource(soundSource);\n this.el.emit('sound-source-set', {\n soundSource: soundSource\n });\n }\n\n this.stream = newStream;\n }\n },\n _setPannerProperties: function _setPannerProperties() {\n if (this.sound && this.data.positional) {\n this.sound.setDistanceModel(this.data.distanceModel);\n this.sound.setMaxDistance(this.data.maxDistance);\n this.sound.setRefDistance(this.data.refDistance);\n this.sound.setRolloffFactor(this.data.rolloffFactor);\n }\n },\n remove: function remove() {\n if (!this.sound) return;\n this.el.removeObject3D(this.attrName);\n\n if (this.stream) {\n this.sound.disconnect();\n }\n\n if (this.audioEl) {\n this.audioEl.pause();\n this.audioEl.srcObject = null;\n this.audioEl.load();\n this.audioEl = null;\n }\n },\n setupSound: function setupSound() {\n var el = this.el;\n var sceneEl = el.sceneEl;\n\n if (this.sound) {\n el.removeObject3D(this.attrName);\n }\n\n if (!sceneEl.audioListener) {\n sceneEl.audioListener = new THREE.AudioListener();\n sceneEl.camera && sceneEl.camera.add(sceneEl.audioListener);\n sceneEl.addEventListener('camera-set-active', function (evt) {\n evt.detail.cameraEl.getObject3D('camera').add(sceneEl.audioListener);\n });\n }\n\n this.listener = sceneEl.audioListener;\n this.sound = this.data.positional ? new THREE.PositionalAudio(this.listener) : new THREE.Audio(this.listener);\n el.setObject3D(this.attrName, this.sound);\n\n this._setPannerProperties();\n }\n});\n\n//# sourceURL=webpack:///./src/components/networked-audio-source.js?"); /***/ }), @@ -310,7 +310,7 @@ eval("\n\n/* global AFRAME, NAF, THREE */\nvar naf = __webpack_require__(/*! ../ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\n/* global AFRAME, NAF, THREE */\nvar deepEqual = __webpack_require__(/*! ../DeepEquals */ \"./src/DeepEquals.js\");\n\nvar InterpolationBuffer = __webpack_require__(/*! buffered-interpolation */ \"./node_modules/buffered-interpolation/dist/buffered-interpolation.js\");\n\nvar DEG2RAD = THREE.Math.DEG2RAD;\nvar OBJECT3D_COMPONENTS = ['position', 'rotation', 'scale'];\n\nfunction defaultRequiresUpdate() {\n var cachedData = null;\n return function (newData) {\n if (cachedData === null || !deepEqual(cachedData, newData)) {\n cachedData = AFRAME.utils.clone(newData);\n return true;\n }\n\n return false;\n };\n}\n\nfunction isValidVector3(v) {\n return !!(v.isVector3 && !isNaN(v.x) && !isNaN(v.y) && !isNaN(v.z) && v.x !== null && v.y !== null && v.z !== null);\n}\n\nfunction isValidQuaternion(q) {\n return !!(q.isQuaternion && !isNaN(q.x) && !isNaN(q.y) && !isNaN(q.z) && !isNaN(q.w) && q.x !== null && q.y !== null && q.z !== null && q.w !== null);\n}\n\nvar throttle = function () {\n var previousLogTime = 0;\n return function throttle(f, milliseconds) {\n var now = Date.now();\n\n if (now - previousLogTime > milliseconds) {\n previousLogTime = now;\n f();\n }\n };\n}();\n\nfunction warnOnInvalidNetworkUpdate() {\n NAF.log.warn(\"Received invalid network update.\");\n}\n\nAFRAME.registerSystem(\"networked\", {\n init: function init() {\n // An array of \"networked\" component instances.\n this.components = [];\n this.nextSyncTime = 0;\n },\n register: function register(component) {\n this.components.push(component);\n },\n deregister: function deregister(component) {\n var idx = this.components.indexOf(component);\n\n if (idx > -1) {\n this.components.splice(idx, 1);\n }\n },\n tick: function () {\n return function () {\n if (!NAF.connection.adapter) return;\n if (this.el.clock.elapsedTime < this.nextSyncTime) return; // \"d\" is an array of entity datas per entity in this.components.\n\n var data = {\n d: []\n };\n\n for (var i = 0, l = this.components.length; i < l; i++) {\n var c = this.components[i];\n if (!c.isMine()) continue;\n\n if (!c.el.parentElement) {\n NAF.log.error(\"entity registered with system despite being removed\"); //TODO: Find out why tick is still being called\n\n return;\n }\n\n var syncData = this.components[i].syncDirty();\n if (!syncData) continue;\n data.d.push(syncData);\n }\n\n if (data.d.length > 0) {\n NAF.connection.broadcastData('um', data);\n }\n\n this.updateNextSyncTime();\n };\n }(),\n updateNextSyncTime: function updateNextSyncTime() {\n this.nextSyncTime = this.el.clock.elapsedTime + 1 / NAF.options.updateRate;\n }\n});\nAFRAME.registerComponent('networked', {\n schema: {\n template: {\n \"default\": ''\n },\n attachTemplateToLocal: {\n \"default\": true\n },\n persistent: {\n \"default\": false\n },\n networkId: {\n \"default\": ''\n },\n owner: {\n \"default\": ''\n },\n creator: {\n \"default\": ''\n }\n },\n init: function init() {\n this.OWNERSHIP_GAINED = 'ownership-gained';\n this.OWNERSHIP_CHANGED = 'ownership-changed';\n this.OWNERSHIP_LOST = 'ownership-lost';\n this.onOwnershipGainedEvent = {\n el: this.el\n };\n this.onOwnershipChangedEvent = {\n el: this.el\n };\n this.onOwnershipLostEvent = {\n el: this.el\n };\n this.conversionEuler = new THREE.Euler();\n this.conversionEuler.order = \"YXZ\";\n this.bufferInfos = [];\n this.bufferPosition = new THREE.Vector3();\n this.bufferQuaternion = new THREE.Quaternion();\n this.bufferScale = new THREE.Vector3();\n var wasCreatedByNetwork = this.wasCreatedByNetwork();\n this.onConnected = this.onConnected.bind(this);\n this.syncData = {};\n this.componentSchemas = NAF.schemas.getComponents(this.data.template);\n this.cachedElements = new Array(this.componentSchemas.length);\n this.networkUpdatePredicates = this.componentSchemas.map(function (x) {\n return x.requiresNetworkUpdate && x.requiresNetworkUpdate() || defaultRequiresUpdate();\n }); // Fill cachedElements array with null elements\n\n this.invalidateCachedElements();\n this.initNetworkParent();\n var networkId;\n\n if (this.data.networkId === '') {\n networkId = NAF.utils.createNetworkId();\n this.el.setAttribute(this.name, {\n networkId: networkId\n });\n } else {\n networkId = this.data.networkId;\n }\n\n if (!this.el.id) {\n this.el.setAttribute('id', 'naf-' + networkId);\n }\n\n if (wasCreatedByNetwork) {\n this.firstUpdate();\n } else {\n if (this.data.attachTemplateToLocal) {\n this.attachTemplateToLocal();\n }\n\n this.registerEntity(this.data.networkId);\n }\n\n this.lastOwnerTime = -1;\n\n if (NAF.clientId) {\n this.onConnected();\n } else {\n document.body.addEventListener('connected', this.onConnected, false);\n }\n\n document.body.dispatchEvent(this.entityCreatedEvent());\n this.el.dispatchEvent(new CustomEvent('instantiated', {\n detail: {\n el: this.el\n }\n }));\n this.el.sceneEl.systems.networked.register(this);\n },\n attachTemplateToLocal: function attachTemplateToLocal() {\n var template = NAF.schemas.getCachedTemplate(this.data.template);\n var elAttrs = template.attributes; // Merge root element attributes with this entity\n\n for (var attrIdx = 0; attrIdx < elAttrs.length; attrIdx++) {\n this.el.setAttribute(elAttrs[attrIdx].name, elAttrs[attrIdx].value);\n } // Append all child elements\n\n\n while (template.firstElementChild) {\n this.el.appendChild(template.firstElementChild);\n }\n },\n takeOwnership: function takeOwnership() {\n var owner = this.data.owner;\n var lastOwnerTime = this.lastOwnerTime;\n var now = NAF.connection.getServerTime();\n\n if (owner && !this.isMine() && lastOwnerTime < now) {\n this.lastOwnerTime = now;\n this.removeLerp();\n this.el.setAttribute('networked', {\n owner: NAF.clientId\n });\n this.syncAll();\n this.onOwnershipGainedEvent.oldOwner = owner;\n this.el.emit(this.OWNERSHIP_GAINED, this.onOwnershipGainedEvent);\n this.onOwnershipChangedEvent.oldOwner = owner;\n this.onOwnershipChangedEvent.newOwner = NAF.clientId;\n this.el.emit(this.OWNERSHIP_CHANGED, this.onOwnershipChangedEvent);\n return true;\n }\n\n return false;\n },\n wasCreatedByNetwork: function wasCreatedByNetwork() {\n return !!this.el.firstUpdateData;\n },\n initNetworkParent: function initNetworkParent() {\n var parentEl = this.el.parentElement;\n\n if (parentEl['components'] && parentEl.components['networked']) {\n this.parent = parentEl;\n } else {\n this.parent = null;\n }\n },\n registerEntity: function registerEntity(networkId) {\n NAF.entities.registerEntity(networkId, this.el);\n },\n applyPersistentFirstSync: function applyPersistentFirstSync() {\n var networkId = this.data.networkId;\n var persistentFirstSync = NAF.entities.getPersistentFirstSync(networkId);\n\n if (persistentFirstSync) {\n this.networkUpdate(persistentFirstSync);\n NAF.entities.forgetPersistentFirstSync(networkId);\n }\n },\n firstUpdate: function firstUpdate() {\n var entityData = this.el.firstUpdateData;\n this.networkUpdate(entityData);\n },\n onConnected: function onConnected() {\n var _this = this;\n\n if (this.data.owner === '') {\n this.lastOwnerTime = NAF.connection.getServerTime();\n this.el.setAttribute(this.name, {\n owner: NAF.clientId,\n creator: NAF.clientId\n });\n setTimeout(function () {\n //a-primitives attach their components on the next frame; wait for components to be attached before calling syncAll\n if (!_this.el.parentNode) {\n NAF.log.warn(\"Networked element was removed before ever getting the chance to syncAll\");\n return;\n }\n\n _this.syncAll(undefined, true);\n }, 0);\n }\n\n document.body.removeEventListener('connected', this.onConnected, false);\n },\n isMine: function isMine() {\n return this.data.owner === NAF.clientId;\n },\n createdByMe: function createdByMe() {\n return this.data.creator === NAF.clientId;\n },\n tick: function tick(time, dt) {\n if (!this.isMine() && NAF.options.useLerp) {\n for (var i = 0; i < this.bufferInfos.length; i++) {\n var bufferInfo = this.bufferInfos[i];\n var buffer = bufferInfo.buffer;\n var object3D = bufferInfo.object3D;\n var componentNames = bufferInfo.componentNames;\n buffer.update(dt);\n\n if (componentNames.includes(\"position\")) {\n var position = buffer.getPosition();\n\n if (isValidVector3(position)) {\n object3D.position.copy(position);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n\n if (componentNames.includes(\"rotation\")) {\n var quaternion = buffer.getQuaternion();\n\n if (isValidQuaternion(quaternion)) {\n object3D.quaternion.copy(quaternion);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n\n if (componentNames.includes(\"scale\")) {\n var scale = buffer.getScale();\n\n if (isValidVector3(scale)) {\n object3D.scale.copy(scale);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n }\n }\n },\n\n /* Sending updates */\n syncAll: function syncAll(targetClientId, isFirstSync) {\n if (!this.canSync()) {\n return;\n }\n\n var components = this.gatherComponentsData(true);\n var syncData = this.createSyncData(components, isFirstSync);\n\n if (targetClientId) {\n NAF.connection.sendDataGuaranteed(targetClientId, 'u', syncData);\n } else {\n NAF.connection.broadcastDataGuaranteed('u', syncData);\n }\n },\n syncDirty: function syncDirty() {\n if (!this.canSync()) {\n return;\n }\n\n var components = this.gatherComponentsData(false);\n\n if (components === null) {\n return;\n }\n\n return this.createSyncData(components);\n },\n getCachedElement: function getCachedElement(componentSchemaIndex) {\n var cachedElement = this.cachedElements[componentSchemaIndex];\n\n if (cachedElement) {\n return cachedElement;\n }\n\n var componentSchema = this.componentSchemas[componentSchemaIndex];\n\n if (componentSchema.selector) {\n return this.cachedElements[componentSchemaIndex] = this.el.querySelector(componentSchema.selector);\n } else {\n return this.cachedElements[componentSchemaIndex] = this.el;\n }\n },\n invalidateCachedElements: function invalidateCachedElements() {\n for (var i = 0; i < this.cachedElements.length; i++) {\n this.cachedElements[i] = null;\n }\n },\n gatherComponentsData: function gatherComponentsData(fullSync) {\n var componentsData = null;\n\n for (var i = 0; i < this.componentSchemas.length; i++) {\n var componentSchema = this.componentSchemas[i];\n var componentElement = this.getCachedElement(i);\n\n if (!componentElement) {\n if (fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = null;\n }\n\n continue;\n }\n\n var componentName = componentSchema.component ? componentSchema.component : componentSchema;\n var componentData = componentElement.getAttribute(componentName);\n\n if (componentData === null) {\n if (fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = null;\n }\n\n continue;\n }\n\n var syncedComponentData = componentSchema.property ? componentData[componentSchema.property] : componentData; // Use networkUpdatePredicate to check if the component needs to be updated.\n // Call networkUpdatePredicate first so that it can update any cached values in the event of a fullSync.\n\n if (this.networkUpdatePredicates[i](syncedComponentData) || fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = syncedComponentData;\n }\n }\n\n return componentsData;\n },\n createSyncData: function createSyncData(components, isFirstSync) {\n var syncData = this.syncData,\n data = this.data;\n syncData.networkId = data.networkId;\n syncData.owner = data.owner;\n syncData.creator = data.creator;\n syncData.lastOwnerTime = this.lastOwnerTime;\n syncData.template = data.template;\n syncData.persistent = data.persistent;\n syncData.parent = this.getParentId();\n syncData.components = components;\n syncData.isFirstSync = !!isFirstSync;\n return syncData;\n },\n canSync: function canSync() {\n // This client will send a sync if:\n //\n // - The client is the owner\n // - The client is the creator, and the owner is not in the room.\n //\n // The reason for the latter case is so the object will still be\n // properly instantiated if the owner leaves. (Since the object lifetime\n // is tied to the creator.)\n if (this.data.owner && this.isMine()) return true;\n if (!this.createdByMe()) return false;\n var clients = NAF.connection.getConnectedClients();\n\n for (var clientId in clients) {\n if (clientId === this.data.owner) return false;\n }\n\n return true;\n },\n getParentId: function getParentId() {\n this.initNetworkParent(); // TODO fix calling this each network tick\n\n if (!this.parent) {\n return null;\n }\n\n var netComp = this.parent.getAttribute('networked');\n return netComp.networkId;\n },\n\n /* Receiving updates */\n networkUpdate: function networkUpdate(entityData) {\n // Avoid updating components if the entity data received did not come from the current owner.\n if (entityData.lastOwnerTime < this.lastOwnerTime || this.lastOwnerTime === entityData.lastOwnerTime && this.data.owner > entityData.owner) {\n return;\n } // Hack to solve this bug: https://github.com/networked-aframe/networked-aframe/issues/200\n\n\n if (this.data === undefined) {\n return;\n }\n\n if (this.data.owner !== entityData.owner) {\n var wasMine = this.isMine();\n this.lastOwnerTime = entityData.lastOwnerTime;\n var oldOwner = this.data.owner;\n var newOwner = entityData.owner;\n this.el.setAttribute('networked', {\n owner: entityData.owner\n });\n\n if (wasMine) {\n this.onOwnershipLostEvent.newOwner = newOwner;\n this.el.emit(this.OWNERSHIP_LOST, this.onOwnershipLostEvent);\n }\n\n this.onOwnershipChangedEvent.oldOwner = oldOwner;\n this.onOwnershipChangedEvent.newOwner = newOwner;\n this.el.emit(this.OWNERSHIP_CHANGED, this.onOwnershipChangedEvent);\n }\n\n if (this.data.persistent !== entityData.persistent) {\n this.el.setAttribute('networked', {\n persistent: entityData.persistent\n });\n }\n\n this.updateNetworkedComponents(entityData.components);\n },\n updateNetworkedComponents: function updateNetworkedComponents(components) {\n for (var componentIndex = 0, l = this.componentSchemas.length; componentIndex < l; componentIndex++) {\n var componentData = components[componentIndex];\n var componentSchema = this.componentSchemas[componentIndex];\n var componentElement = this.getCachedElement(componentIndex);\n\n if (componentElement === null || componentData === null || componentData === undefined) {\n continue;\n }\n\n if (componentSchema.component) {\n if (componentSchema.property) {\n this.updateNetworkedComponent(componentElement, componentSchema.component, componentSchema.property, componentData);\n } else {\n this.updateNetworkedComponent(componentElement, componentSchema.component, componentData);\n }\n } else {\n this.updateNetworkedComponent(componentElement, componentSchema, componentData);\n }\n }\n },\n updateNetworkedComponent: function updateNetworkedComponent(el, componentName, data, value) {\n if (!NAF.options.useLerp || !OBJECT3D_COMPONENTS.includes(componentName)) {\n if (value === undefined) {\n el.setAttribute(componentName, data);\n } else {\n el.setAttribute(componentName, data, value);\n }\n\n return;\n }\n\n var bufferInfo;\n\n for (var i = 0, l = this.bufferInfos.length; i < l; i++) {\n var info = this.bufferInfos[i];\n\n if (info.object3D === el.object3D) {\n bufferInfo = info;\n break;\n }\n }\n\n if (!bufferInfo) {\n bufferInfo = {\n buffer: new InterpolationBuffer(InterpolationBuffer.MODE_LERP, 0.1),\n object3D: el.object3D,\n componentNames: [componentName]\n };\n this.bufferInfos.push(bufferInfo);\n } else {\n var componentNames = bufferInfo.componentNames;\n\n if (!componentNames.includes(componentName)) {\n componentNames.push(componentName);\n }\n }\n\n var buffer = bufferInfo.buffer;\n\n switch (componentName) {\n case 'position':\n buffer.setPosition(this.bufferPosition.set(data.x, data.y, data.z));\n return;\n\n case 'rotation':\n this.conversionEuler.set(DEG2RAD * data.x, DEG2RAD * data.y, DEG2RAD * data.z);\n buffer.setQuaternion(this.bufferQuaternion.setFromEuler(this.conversionEuler));\n return;\n\n case 'scale':\n buffer.setScale(this.bufferScale.set(data.x, data.y, data.z));\n return;\n }\n\n NAF.log.error('Could not set value in interpolation buffer.', el, componentName, data, bufferInfo);\n },\n removeLerp: function removeLerp() {\n this.bufferInfos = [];\n },\n remove: function remove() {\n if (this.isMine() && NAF.connection.isConnected()) {\n var syncData = {\n networkId: this.data.networkId\n };\n\n if (NAF.entities.hasEntity(this.data.networkId)) {\n NAF.connection.broadcastDataGuaranteed('r', syncData);\n } else {\n NAF.log.error(\"Removing networked entity that is not in entities array.\");\n }\n }\n\n NAF.entities.forgetEntity(this.data.networkId);\n document.body.dispatchEvent(this.entityRemovedEvent(this.data.networkId));\n this.el.sceneEl.systems.networked.deregister(this);\n },\n entityCreatedEvent: function entityCreatedEvent() {\n return new CustomEvent('entityCreated', {\n detail: {\n el: this.el\n }\n });\n },\n entityRemovedEvent: function entityRemovedEvent(networkId) {\n return new CustomEvent('entityRemoved', {\n detail: {\n networkId: networkId\n }\n });\n }\n});\n\n//# sourceURL=webpack:///./src/components/networked.js?"); +eval("\n\n/* global AFRAME, NAF, THREE */\nvar deepEqual = __webpack_require__(/*! ../DeepEquals */ \"./src/DeepEquals.js\");\n\nvar InterpolationBuffer = __webpack_require__(/*! buffered-interpolation */ \"./node_modules/buffered-interpolation/dist/buffered-interpolation.js\");\n\nvar DEG2RAD = THREE.Math.DEG2RAD;\nvar OBJECT3D_COMPONENTS = ['position', 'rotation', 'scale'];\n\nfunction defaultRequiresUpdate() {\n var cachedData = null;\n return function (newData) {\n if (cachedData === null || !deepEqual(cachedData, newData)) {\n cachedData = AFRAME.utils.clone(newData);\n return true;\n }\n\n return false;\n };\n}\n\nfunction isValidVector3(v) {\n return !!(v.isVector3 && !isNaN(v.x) && !isNaN(v.y) && !isNaN(v.z) && v.x !== null && v.y !== null && v.z !== null);\n}\n\nfunction isValidQuaternion(q) {\n return !!(q.isQuaternion && !isNaN(q.x) && !isNaN(q.y) && !isNaN(q.z) && !isNaN(q.w) && q.x !== null && q.y !== null && q.z !== null && q.w !== null);\n}\n\nvar throttle = function () {\n var previousLogTime = 0;\n return function throttle(f, milliseconds) {\n var now = Date.now();\n\n if (now - previousLogTime > milliseconds) {\n previousLogTime = now;\n f();\n }\n };\n}();\n\nfunction warnOnInvalidNetworkUpdate() {\n NAF.log.warn(\"Received invalid network update.\");\n}\n\nAFRAME.registerSystem(\"networked\", {\n init: function init() {\n // An array of \"networked\" component instances.\n this.components = [];\n this.nextSyncTime = 0;\n },\n register: function register(component) {\n this.components.push(component);\n },\n deregister: function deregister(component) {\n var idx = this.components.indexOf(component);\n\n if (idx > -1) {\n this.components.splice(idx, 1);\n }\n },\n tick: function () {\n return function () {\n if (!NAF.connection.adapter) return;\n if (this.el.clock.elapsedTime < this.nextSyncTime) return; // \"d\" is an array of entity datas per entity in this.components.\n\n var data = {\n d: []\n };\n\n for (var i = 0, l = this.components.length; i < l; i++) {\n var c = this.components[i];\n if (!c.isMine()) continue;\n\n if (!c.el.parentElement) {\n NAF.log.error(\"entity registered with system despite being removed\"); //TODO: Find out why tick is still being called\n\n return;\n }\n\n var syncData = this.components[i].syncDirty();\n if (!syncData) continue;\n data.d.push(syncData);\n }\n\n if (data.d.length > 0) {\n NAF.connection.broadcastData('um', data);\n }\n\n this.updateNextSyncTime();\n };\n }(),\n updateNextSyncTime: function updateNextSyncTime() {\n this.nextSyncTime = this.el.clock.elapsedTime + 1 / NAF.options.updateRate;\n }\n});\nAFRAME.registerComponent('networked', {\n schema: {\n template: {\n \"default\": ''\n },\n attachTemplateToLocal: {\n \"default\": true\n },\n persistent: {\n \"default\": false\n },\n networkId: {\n \"default\": ''\n },\n owner: {\n \"default\": ''\n },\n creator: {\n \"default\": ''\n }\n },\n init: function init() {\n this.OWNERSHIP_GAINED = 'ownership-gained';\n this.OWNERSHIP_CHANGED = 'ownership-changed';\n this.OWNERSHIP_LOST = 'ownership-lost';\n this.onOwnershipGainedEvent = {\n el: this.el\n };\n this.onOwnershipChangedEvent = {\n el: this.el\n };\n this.onOwnershipLostEvent = {\n el: this.el\n };\n this.conversionEuler = new THREE.Euler();\n this.conversionEuler.order = \"YXZ\";\n this.bufferInfos = [];\n this.bufferPosition = new THREE.Vector3();\n this.bufferQuaternion = new THREE.Quaternion();\n this.bufferScale = new THREE.Vector3();\n var wasCreatedByNetwork = this.wasCreatedByNetwork();\n this.onConnected = this.onConnected.bind(this);\n this.syncData = {};\n this.componentSchemas = NAF.schemas.getComponents(this.data.template);\n this.cachedElements = new Array(this.componentSchemas.length);\n this.networkUpdatePredicates = this.componentSchemas.map(function (x) {\n return x.requiresNetworkUpdate && x.requiresNetworkUpdate() || defaultRequiresUpdate();\n }); // Fill cachedElements array with null elements\n\n this.invalidateCachedElements();\n this.initNetworkParent();\n var networkId;\n\n if (this.data.networkId === '') {\n networkId = NAF.utils.createNetworkId();\n this.el.setAttribute(this.name, {\n networkId: networkId\n });\n } else {\n networkId = this.data.networkId;\n }\n\n if (!this.el.id) {\n this.el.setAttribute('id', 'naf-' + networkId);\n }\n\n if (wasCreatedByNetwork) {\n this.firstUpdate();\n } else {\n if (this.data.attachTemplateToLocal) {\n this.attachTemplateToLocal();\n }\n\n this.registerEntity(this.data.networkId);\n }\n\n this.lastOwnerTime = -1;\n\n if (NAF.clientId) {\n this.onConnected();\n } else {\n document.body.addEventListener('connected', this.onConnected, false);\n }\n\n document.body.dispatchEvent(this.entityCreatedEvent());\n this.el.dispatchEvent(new CustomEvent('instantiated', {\n detail: {\n el: this.el\n }\n }));\n this.el.sceneEl.systems.networked.register(this);\n },\n attachTemplateToLocal: function attachTemplateToLocal() {\n var template = NAF.schemas.getCachedTemplate(this.data.template);\n var elAttrs = template.attributes; // Merge root element attributes with this entity\n\n for (var attrIdx = 0; attrIdx < elAttrs.length; attrIdx++) {\n this.el.setAttribute(elAttrs[attrIdx].name, elAttrs[attrIdx].value);\n } // Append all child elements\n\n\n while (template.firstElementChild) {\n this.el.appendChild(template.firstElementChild);\n }\n },\n takeOwnership: function takeOwnership() {\n var owner = this.data.owner;\n var lastOwnerTime = this.lastOwnerTime;\n var now = NAF.connection.getServerTime();\n\n if (owner && !this.isMine() && lastOwnerTime < now) {\n this.lastOwnerTime = now;\n this.removeLerp();\n this.el.setAttribute('networked', {\n owner: NAF.clientId\n });\n this.syncAll();\n this.onOwnershipGainedEvent.oldOwner = owner;\n this.el.emit(this.OWNERSHIP_GAINED, this.onOwnershipGainedEvent);\n this.onOwnershipChangedEvent.oldOwner = owner;\n this.onOwnershipChangedEvent.newOwner = NAF.clientId;\n this.el.emit(this.OWNERSHIP_CHANGED, this.onOwnershipChangedEvent);\n return true;\n }\n\n return false;\n },\n wasCreatedByNetwork: function wasCreatedByNetwork() {\n return !!this.el.firstUpdateData;\n },\n initNetworkParent: function initNetworkParent() {\n var parentEl = this.el.parentElement;\n\n if (parentEl['components'] && parentEl.components['networked']) {\n this.parent = parentEl;\n } else {\n this.parent = null;\n }\n },\n registerEntity: function registerEntity(networkId) {\n NAF.entities.registerEntity(networkId, this.el);\n },\n applyPersistentFirstSync: function applyPersistentFirstSync() {\n var networkId = this.data.networkId;\n var persistentFirstSync = NAF.entities.getPersistentFirstSync(networkId);\n\n if (persistentFirstSync) {\n this.networkUpdate(persistentFirstSync);\n NAF.entities.forgetPersistentFirstSync(networkId);\n }\n },\n firstUpdate: function firstUpdate() {\n var entityData = this.el.firstUpdateData;\n this.networkUpdate(entityData);\n },\n onConnected: function onConnected() {\n var _this = this;\n\n if (this.data.owner === '') {\n this.lastOwnerTime = NAF.connection.getServerTime();\n this.el.setAttribute(this.name, {\n owner: NAF.clientId,\n creator: NAF.clientId\n });\n setTimeout(function () {\n //a-primitives attach their components on the next frame; wait for components to be attached before calling syncAll\n if (!_this.el.parentNode) {\n NAF.log.warn(\"Networked element was removed before ever getting the chance to syncAll\");\n return;\n }\n\n _this.syncAll(undefined, true);\n }, 0);\n }\n\n document.body.removeEventListener('connected', this.onConnected, false);\n },\n isMine: function isMine() {\n return this.data.owner === NAF.clientId;\n },\n createdByMe: function createdByMe() {\n return this.data.creator === NAF.clientId;\n },\n tick: function tick(time, dt) {\n if (!this.isMine() && NAF.options.useLerp) {\n for (var i = 0; i < this.bufferInfos.length; i++) {\n var bufferInfo = this.bufferInfos[i];\n var buffer = bufferInfo.buffer;\n var object3D = bufferInfo.object3D;\n var componentNames = bufferInfo.componentNames;\n buffer.update(dt);\n\n if (componentNames.includes(\"position\")) {\n var position = buffer.getPosition();\n\n if (isValidVector3(position)) {\n object3D.position.copy(position);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n\n if (componentNames.includes(\"rotation\")) {\n var quaternion = buffer.getQuaternion();\n\n if (isValidQuaternion(quaternion)) {\n object3D.quaternion.copy(quaternion);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n\n if (componentNames.includes(\"scale\")) {\n var scale = buffer.getScale();\n\n if (isValidVector3(scale)) {\n object3D.scale.copy(scale);\n } else {\n throttle(warnOnInvalidNetworkUpdate, 5000);\n }\n }\n }\n }\n },\n\n /* Sending updates */\n syncAll: function syncAll(targetClientId, isFirstSync) {\n if (!this.canSync()) {\n return;\n }\n\n var components = this.gatherComponentsData(true);\n var syncData = this.createSyncData(components, isFirstSync);\n\n if (targetClientId) {\n NAF.connection.sendDataGuaranteed(targetClientId, 'u', syncData);\n } else {\n NAF.connection.broadcastDataGuaranteed('u', syncData);\n }\n },\n syncDirty: function syncDirty() {\n if (!this.canSync()) {\n return;\n }\n\n var components = this.gatherComponentsData(false);\n\n if (components === null) {\n return;\n }\n\n return this.createSyncData(components);\n },\n getCachedElement: function getCachedElement(componentSchemaIndex) {\n var cachedElement = this.cachedElements[componentSchemaIndex];\n\n if (cachedElement) {\n return cachedElement;\n }\n\n var componentSchema = this.componentSchemas[componentSchemaIndex];\n\n if (componentSchema.selector) {\n return this.cachedElements[componentSchemaIndex] = this.el.querySelector(componentSchema.selector);\n } else {\n return this.cachedElements[componentSchemaIndex] = this.el;\n }\n },\n invalidateCachedElements: function invalidateCachedElements() {\n for (var i = 0; i < this.cachedElements.length; i++) {\n this.cachedElements[i] = null;\n }\n },\n gatherComponentsData: function gatherComponentsData(fullSync) {\n var componentsData = null;\n\n for (var i = 0; i < this.componentSchemas.length; i++) {\n var componentSchema = this.componentSchemas[i];\n var componentElement = this.getCachedElement(i);\n\n if (!componentElement) {\n if (fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = null;\n }\n\n continue;\n }\n\n var componentName = componentSchema.component ? componentSchema.component : componentSchema;\n var componentData = componentElement.getAttribute(componentName);\n\n if (componentData === null) {\n if (fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = null;\n }\n\n continue;\n }\n\n var syncedComponentData = componentSchema.property ? componentData[componentSchema.property] : componentData; // Use networkUpdatePredicate to check if the component needs to be updated.\n // Call networkUpdatePredicate first so that it can update any cached values in the event of a fullSync.\n\n if (this.networkUpdatePredicates[i](syncedComponentData) || fullSync) {\n componentsData = componentsData || {};\n componentsData[i] = syncedComponentData;\n }\n }\n\n return componentsData;\n },\n createSyncData: function createSyncData(components, isFirstSync) {\n var syncData = this.syncData,\n data = this.data;\n syncData.networkId = data.networkId;\n syncData.owner = data.owner;\n syncData.creator = data.creator;\n syncData.lastOwnerTime = this.lastOwnerTime;\n syncData.template = data.template;\n syncData.persistent = data.persistent;\n syncData.parent = this.getParentId();\n syncData.components = components;\n syncData.isFirstSync = !!isFirstSync;\n return syncData;\n },\n canSync: function canSync() {\n // This client will send a sync if:\n //\n // - The client is the owner\n // - The client is the creator, and the owner is not in the room.\n //\n // The reason for the latter case is so the object will still be\n // properly instantiated if the owner leaves. (Since the object lifetime\n // is tied to the creator.)\n if (this.data.owner && this.isMine()) return true;\n if (!this.createdByMe()) return false;\n var clients = NAF.connection.getConnectedClients();\n\n for (var clientId in clients) {\n if (clientId === this.data.owner) return false;\n }\n\n return true;\n },\n getParentId: function getParentId() {\n this.initNetworkParent(); // TODO fix calling this each network tick\n\n if (!this.parent) {\n return null;\n }\n\n var netComp = this.parent.getAttribute('networked');\n return netComp.networkId;\n },\n\n /* Receiving updates */\n networkUpdate: function networkUpdate(entityData) {\n // Avoid updating components if the entity data received did not come from the current owner.\n if (entityData.lastOwnerTime < this.lastOwnerTime || this.lastOwnerTime === entityData.lastOwnerTime && this.data.owner > entityData.owner) {\n return;\n } // Hack to solve this bug: https://github.com/networked-aframe/networked-aframe/issues/200\n\n\n if (this.data === undefined) {\n return;\n }\n\n if (this.data.owner !== entityData.owner) {\n var wasMine = this.isMine();\n this.lastOwnerTime = entityData.lastOwnerTime;\n var oldOwner = this.data.owner;\n var newOwner = entityData.owner;\n this.el.setAttribute('networked', {\n owner: entityData.owner\n });\n\n if (wasMine) {\n this.onOwnershipLostEvent.newOwner = newOwner;\n this.el.emit(this.OWNERSHIP_LOST, this.onOwnershipLostEvent);\n }\n\n this.onOwnershipChangedEvent.oldOwner = oldOwner;\n this.onOwnershipChangedEvent.newOwner = newOwner;\n this.el.emit(this.OWNERSHIP_CHANGED, this.onOwnershipChangedEvent);\n }\n\n if (this.data.persistent !== entityData.persistent) {\n this.el.setAttribute('networked', {\n persistent: entityData.persistent\n });\n }\n\n this.updateNetworkedComponents(entityData.components);\n },\n updateNetworkedComponents: function updateNetworkedComponents(components) {\n for (var componentIndex = 0, l = this.componentSchemas.length; componentIndex < l; componentIndex++) {\n var componentData = components[componentIndex];\n var componentSchema = this.componentSchemas[componentIndex];\n var componentElement = this.getCachedElement(componentIndex);\n\n if (componentElement === null || componentData === null || componentData === undefined) {\n continue;\n }\n\n if (componentSchema.component) {\n if (componentSchema.property) {\n this.updateNetworkedComponent(componentElement, componentSchema.component, componentSchema.property, componentData);\n } else {\n this.updateNetworkedComponent(componentElement, componentSchema.component, componentData);\n }\n } else {\n this.updateNetworkedComponent(componentElement, componentSchema, componentData);\n }\n }\n },\n updateNetworkedComponent: function updateNetworkedComponent(el, componentName, data, value) {\n if (!NAF.options.useLerp || !OBJECT3D_COMPONENTS.includes(componentName)) {\n if (value === undefined) {\n el.setAttribute(componentName, data);\n } else {\n el.setAttribute(componentName, data, value);\n }\n\n return;\n }\n\n var bufferInfo;\n\n for (var i = 0, l = this.bufferInfos.length; i < l; i++) {\n var info = this.bufferInfos[i];\n\n if (info.object3D === el.object3D) {\n bufferInfo = info;\n break;\n }\n }\n\n if (!bufferInfo) {\n bufferInfo = {\n buffer: new InterpolationBuffer(InterpolationBuffer.MODE_LERP, 0.1),\n object3D: el.object3D,\n componentNames: [componentName]\n };\n this.bufferInfos.push(bufferInfo);\n } else {\n var componentNames = bufferInfo.componentNames;\n\n if (!componentNames.includes(componentName)) {\n componentNames.push(componentName);\n }\n }\n\n var buffer = bufferInfo.buffer;\n\n switch (componentName) {\n case 'position':\n buffer.setPosition(this.bufferPosition.set(data.x, data.y, data.z));\n return;\n\n case 'rotation':\n this.conversionEuler.set(DEG2RAD * data.x, DEG2RAD * data.y, DEG2RAD * data.z);\n buffer.setQuaternion(this.bufferQuaternion.setFromEuler(this.conversionEuler));\n return;\n\n case 'scale':\n buffer.setScale(this.bufferScale.set(data.x, data.y, data.z));\n return;\n }\n\n NAF.log.error('Could not set value in interpolation buffer.', el, componentName, data, bufferInfo);\n },\n removeLerp: function removeLerp() {\n this.bufferInfos = [];\n },\n remove: function remove() {\n if (this.isMine() && NAF.connection.isConnected()) {\n var syncData = {\n networkId: this.data.networkId\n };\n\n if (NAF.entities.hasEntity(this.data.networkId)) {\n NAF.connection.broadcastDataGuaranteed('r', syncData);\n } else {\n // The entity may already have been removed if the creator (different of the current owner) left the room.\n // Don't log an error in this case.\n if (!(this.data.creator && NAF.connection.activeDataChannels[this.data.creator] === false)) {\n NAF.log.error(\"Removing networked entity that is not in entities array.\");\n }\n }\n }\n\n NAF.entities.forgetEntity(this.data.networkId);\n document.body.dispatchEvent(this.entityRemovedEvent(this.data.networkId));\n this.el.sceneEl.systems.networked.deregister(this);\n },\n entityCreatedEvent: function entityCreatedEvent() {\n return new CustomEvent('entityCreated', {\n detail: {\n el: this.el\n }\n });\n },\n entityRemovedEvent: function entityRemovedEvent(networkId) {\n return new CustomEvent('entityRemoved', {\n detail: {\n networkId: networkId\n }\n });\n }\n});\n\n//# sourceURL=webpack:///./src/components/networked.js?"); /***/ }), diff --git a/dist/networked-aframe.min.js b/dist/networked-aframe.min.js index 21a76e07..bcd29d72 100644 --- a/dist/networked-aframe.min.js +++ b/dist/networked-aframe.min.js @@ -1 +1 @@ -!function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}([function(e,t,n){"use strict";var i=n(3),o=n(4),r=n(5),s=n(6),a=n(7),c=n(9),u=n(10),l={app:"",room:"",clientId:""};l.options=i,l.utils=o,l.log=new r,l.schemas=new s,l.version="0.8.2",l.adapters=new u;var h=new a,d=new c(h);l.connection=d,l.entities=h,e.exports=window.NAF=l},function(e,t,n){"use strict";function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){for(var n=0;n is defined."));if(!this.validateTemplate(e,t))return;this.templateCache[e.template]=document.importNode(t.content,!0)}else NAF.log.error("Schema not valid: ",e),NAF.log.error("See https://github.com/networked-aframe/networked-aframe#syncing-custom-components")}},{key:"getCachedTemplate",value:function(e){return this.templateIsCached(e)||(this.templateExistsInScene(e)?this.add(this.createDefaultSchema(e)):NAF.log.error("Template el for ".concat(e," is not in the scene, add the template to and register with NAF.schemas.add."))),this.templateCache[e].firstElementChild.cloneNode(!0)}},{key:"templateIsCached",value:function(e){return!!this.templateCache[e]}},{key:"getComponents",value:function(e){var t=["position","rotation"];return this.hasTemplate(e)&&(t=this.schemaDict[e].components),t}},{key:"hasTemplate",value:function(e){return!!this.schemaDict[e]}},{key:"templateExistsInScene",value:function(e){var t=document.querySelector(e);return t&&this.isTemplateTag(t)}},{key:"validateSchema",value:function(e){return!(!e.template||!e.components)}},{key:"validateTemplate",value:function(e,t){return this.isTemplateTag(t)?!!this.templateHasOneOrZeroChildren(t)||(NAF.log.error("Template for ".concat(e.template," has more than one child. Templates must have one direct child element, no more. Template found:"),t),!1):(NAF.log.error("Template for ".concat(e.template," is not a