the server sends ‘data’ via socket and enqueues it with the queueItem type ‘data’, as the queueItem resource:
// initialize code called once per entity
Network.prototype.initialize = function() {
this.queue = new Queue();
this.isQueueRunning = false;
this.progress = 0;
this.progressExpected = 0;
var socket = io.connect(CYBERS_CAFE_SERVICE_URL);
Network.socket = socket;
socket.on ('addRecord', function (data) {
self.queue.enqueue(new QueueItem('data', data));
if (!self.isQueueRunning) {
self.popQueue();
}
});
//...
furthermore, the network.js file handles the loading of playcanvas entities and assets
//****
// Overall, the popQueue method is responsible for dequeueing items from the queue,
// checking their types, and calling the appropriate methods.
// It also updates the progress bar and handles recursive calls to the popQueue method (subroutines call popQueue() )
Network.prototype.popQueue = function() {
if (!this.isQueueRunning) {
this.isQueueRunning = true;
}
var queueItem = this.queue.dequeue();
if (queueItem) {
console.log("Dequeuing " + queueItem.type + ", queue length: " + this.queue.getLength());
if (queueItem.type == 'asset') {
this.addAsset (queueItem.resource);
} else if (queueItem.type == 'entity') {
this.addEntity (queueItem.resource);
} else if (queueItem.type == 'glbEntity') {
this.addGlbEntity (queueItem.resource);
} else if (queueItem.type == 'record') {
this.addRecord (queueItem.resource);
} else if (queueItem.type == 'data') {
this.addAssetsAndEntitiesAndRecord (queueItem.resource);
} else if (queueItem.type == 'delete') {
this.deleteRecord (queueItem.resource);
} else {
console.log("Unknown QueueItem type: " + queueItem.type);
}
} else {
console.log("Queue is empty");
}
if (this.queue.getLength() === 0) {
this.isQueueRunning = false;
}
};
//****
// this method gets called by popQueue() first,
// and calls addAsset() and addEntity() methods as needed (via the popQueue() method)
Network.prototype.addAssetsAndEntitiesAndRecord = async function(data) {
const self = this;
if (data.record.id in this.app.storage.records) {
console.log('Record already added: ' + data.record.id);
// if the record is present because it was loaded before
// the entities might have been turned off from leaving the loation range
// so we still need to re-add the record
// continue readding the record now
}
console.log('Add Record ' + data.record.id);
if (!(data.record.id in self.app.storage.records)) {
self.queue.enqueue(new QueueItem('record', data.record));
if (!self.isQueueRunning) {
self.popQueue();
}
}
Object.keys(data.assets).forEach(function (assetId) {
const asset = data.assets[assetId];
console.log('Add Asset ' + asset.id);
if (!app.assets.get(asset.id)) {
self.queue.enqueue(new QueueItem('asset', asset));
if (!self.isQueueRunning) {
self.popQueue();
}
}
});
var rootEntity = null;
if (data.record.glbAssetId != null) {
// loads glb entities
self.queue.enqueue(new QueueItem('glbEntity', {asset: data.assets[data.record.glbAssetId]}));
if (!self.isQueueRunning) {
self.popQueue();
}
} else {
// loads classic zip entities
rootEntity = data.entities[data.record.rootEntityId];
// load entity (enqueue locally for loading)
self.queue.enqueue(new QueueItem('entity', {entity: rootEntity, entities: data.entities}));
if (!self.isQueueRunning) {
self.popQueue();
}
}
self.popQueue();
// update the progress bar
this.progress++;
if (this.progressPercent == null) {
this.progressPercent = 0;
$('#progress-inner-div').attr('aria-valuenow', 0).css('width',0);
$('#progress-div').css('visibility', 'visible');
}
if (this.progressExpected) {
const percent = this.progress / this.progressExpected * 100;
if (percent > this.progressPercent) {
this.progressPercent = percent;
$('#progress-inner-div').attr('aria-valuenow', this.progressPercent).css('width',this.progressPercent + '%');
}
}
setTimeout(function(){
if (self.progress >= self.progressExpected) {
self.progress = 0;
self.progressExpected = 0;
self.progressPercent = null;
$('#progress-inner-div').attr('aria-valuenow', 100).css('width','100%');
setTimeout(function(){
$('#progress-div').css('visibility', 'hidden');
$('#progress-inner-div').attr('aria-valuenow', 0).css('width','0%');
},3000);
}
},5000);
}
// *****
// this method is called by addAssetsAndEntitiesAndRecord() via popQueue as needed
Network.prototype.addEntity = function(data) {
var entityData = data.entity;
var entities = data.entities;
var entity = new pc.Entity();
entity = this.prepareEntity(entity, entityData);
this.app.root.addChild(entity);
this.resizeImageEntity(entity);
entity.added = true;
this.addChildren(entity, entities);
this.app.storage.entities[entity.id] = entity;
console.log('Root entity ' + entity.id + ' added');
console.log(entity);
this.popQueue();
};
// ******
// this method is called by addEntity()
Network.prototype.prepareEntity = function(entity, data) {
for (var componentName in data.components) {
if (componentName == "camera" || componentName == "script") {
continue;
}
const component = entity.addComponent(componentName, data.components[componentName]);
if (component == null) {
throw new Error("Unable to add component " + componentName + " to entity " + entity.id);
}
}
//########################################################################################## script loading
// scripts
try {
if (entity.script == null) {
entity.addComponent("script");
}
for (var scriptName in data.components.script.scripts) {
const script = data.components.script.scripts[scriptName];
const attributes = script.attributes;
entity.script.create(scriptName, {"attributes": attributes, "preloading": true});
}
} catch (err) {
console.log(err);
}
// general properties
entity.id = data.resource_id;
entity.resource_id = data.resource_id;
entity.record_id = data.record_id;
entity.title = record.title;
entity.name = data.name;
// (positioning and so on)
// ...
}
// *****
// this method is called by addAssetsAndEntitiesAndRecord() via popQueue() as needed
Network.prototype.addAsset = function(data) {
// from playcanvas application.js: _parseAssets()
var asset = new pc.Asset(data.name, data.type, data.file, data.data);
asset.id = data.id;
asset.preload = data.preload ? data.preload : false;
asset.tags.add(data.tags);
asset.revision = data.revision;
try {
this.app.assets.add(asset);
} catch (err) {
console.log("Asset error: " + err);
return;
}
var self = this;
var onAssetLoad = function(asset) {
self.app.storage.assets[asset.id] = asset;
console.log('Asset ' + asset.id + ' added');
console.log(asset);
self.popQueue();
};
if (asset.resource) {
// The asset has already been loaded
onAssetLoad(asset);
} else {
// Start async loading the asset
asset.once('load', onAssetLoad);
try {
this.app.assets.load(asset);
} catch (err) {
console.log("Asset error: " + err);
}
}
};
you can see the assets are being queued to be loaded first before the entities. i tried it the other way around and it wasnt working either.