Merge branch 'storage_fixing'

beta.r3js.org
-=yb4f310 2017-11-14 12:47:50 +01:00
commit 0d900614df
1 changed files with 276 additions and 284 deletions

View File

@ -70,6 +70,11 @@ GameLib.System.Storage = function(
}
this.onComponentError = onComponentError;
this.loaded = [];
this.loading = [];
this.failed = [];
this.otherDependencies = [];
this.loginSubscription = null;
this.saveSubscription = null;
this.loadSubscription = null;
@ -318,353 +323,340 @@ GameLib.System.Storage.prototype.save = function(data) {
};
GameLib.System.Storage.prototype.loadComponent = function(apiUrl, toProcess, includeDependencies, clientCallback, clientErrorCallback) {
GameLib.System.Storage.prototype.createRuntimeObject = function(responseText, clientErrorCallback) {
var loaded = [];
try {
var object = JSON.parse(responseText);
} catch (errorObject) {
var loading = [];
toProcess.map(function(id) {
if (loading.indexOf(id) === -1) {
loading.push(id);
if (this.onComponentError) {
this.onComponentError(errorObject);
}
});
/**
* We just do an initial check if these components to process are already in the register -
* if so we remove them since we probably want to overwrite them with stale DB versions.
*
* We don't override runtime versions of the dependencies of the loading components - since they could be later.
* But we do override runtime versions of the loading component since the user actually selected them and clicked 'load'
*/
loading.map(
function(id) {
if (clientErrorCallback) {
clientErrorCallback({
message : errorObject.message || 'JSON parse error'
})
}
var component = GameLib.EntityManager.Instance.findComponentById(id);
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{error: errorObject}
);
if (component) {
component.remove();
}
}
);
return null;
}
return function download(id, parentEntity) {
if (object.result !== 'success') {
var onComponentLoaded = this.onComponentLoaded;
if (this.onComponentError) {
this.onComponentError(id, object);
}
var onComponentProgress = this.onComponentProgress;
if (clientErrorCallback) {
clientErrorCallback({
message : object.message || 'Server load error'
})
}
var onComponentError = this.onComponentError;
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{error : object}
);
var xhr = new XMLHttpRequest();
return null;
}
xhr.onload = function(__system) {
var runtimeComponent = null;
return function () {
/**
* Now we need to create the runtime component - this happens systematically.
* First, we create an API object from the Object, then a Runtime object from the API object
* Each component has a function 'FromObject' which essentially does this for you
*/
var component = object.component[0];
var error = false;
var componentName = GameLib.Component.GetComponentName(component.componentType);
var componentClass = eval(componentName);
var fn = componentClass['FromObject'];
if (component.componentType === GameLib.Component.COMPONENT_ENTITY) {
runtimeComponent = fn(component, GameLib.EntityManager.Instance);
} else {
try {
runtimeComponent = fn(component);
} catch (error) {
if (this.coder) {
try {
var object = JSON.parse(this.responseText);
} catch (errorObject) {
runtimeComponent = fn(this.coder, component);
} catch (error) {
if (onComponentError) {
onComponentError(errorObject);
}
if (clientErrorCallback) {
clientErrorCallback({
message : errorObject.message || 'JSON parse error'
})
}
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{
error: errorObject
}
);
error = true;
}
if (!error && object.result !== 'success') {
}
if (onComponentError) {
onComponentError(id, object);
}
if (!runtimeComponent && this.graphics) {
try {
runtimeComponent = fn(this.graphics, component);
} catch (error) {
if (clientErrorCallback) {
clientErrorCallback({
message : object.message || 'Server load error'
})
}
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{error : object}
);
error = true;
}
}
var runtimeComponent = null;
if (!error) {
if (!runtimeComponent && this.physics) {
try {
runtimeComponent = fn(this.physics, component);
} catch (error) {
/**
* Now we need to create the runtime component - this happens systematically.
* First, we create an API object from the Object, then a Runtime object from the API object
* Each component has a function 'FromObject' which essentially does this for you
* ok - we don't cannot create this component
*/
var component = object.component[0];
}
}
var componentName = GameLib.Component.GetComponentName(component.componentType);
}
var componentClass = eval(componentName);
if (!runtimeComponent) {
if (clientErrorCallback) {
clientErrorCallback({
result: 'failure',
message: 'Could not create a runtime component: ' + component.name
});
}
}
var fn = componentClass['FromObject'];
}
if (component.componentType === GameLib.Component.COMPONENT_ENTITY) {
runtimeComponent = fn(component, GameLib.EntityManager.Instance);
runtimeComponent.parentEntity = parentEntity;
parentEntity = runtimeComponent;
} else {
return runtimeComponent;
};
try {
GameLib.System.Storage.prototype.loadComponent = function(apiUrl, toProcess, includeDependencies, clientCallback, clientErrorCallback) {
runtimeComponent = fn(component);
/**
* We just do an initial check if these components to process are already in the register -
* if so we remove them since we probably want to overwrite them with stale DB versions.
*
* We don't override runtime versions of the dependencies of the loading components - since they could be later.
* But we do override runtime versions of the loading component since the user actually selected them and clicked 'load'
*/
toProcess.map(
function(id) {
} catch (error) {
GameLib.Utils.PushUnique(this.loading, id);
if (__system.coder) {
var component = GameLib.EntityManager.Instance.findComponentById(id);
try {
runtimeComponent = fn(__system.coder, component);
} catch (error) {
if (component) {
component.remove();
}
}.bind(this)
);
}
toProcess.map(
}
function(id) {
if (!runtimeComponent && __system.graphics) {
try {
runtimeComponent = fn(__system.graphics, component);
} catch (error) {
var xhr = new XMLHttpRequest();
xhr.onload = function(__system) {
return function () {
var runtimeComponent = __system.createRuntimeObject.bind(__system)(this.responseText);
if (!runtimeComponent) {
__system.failed.push(id);
return;
}
if (
runtimeComponent.parentEntity &&
typeof runtimeComponent.parentEntity === 'string'
) {
GameLib.EntityManager.Instance.queryComponents(GameLib.Entity).map(
function (entity) {
if (runtimeComponent.parentEntity === entity.id) {
runtimeComponent.parentEntity = entity;
}
}
if (!runtimeComponent && __system.physics) {
try {
runtimeComponent = fn(__system.physics, component);
} catch (error) {
/**
* ok - we don't cannot create this component
*/
}
}
}
if (!runtimeComponent) {
if (clientErrorCallback) {
clientErrorCallback({
result: 'failure',
message: 'Could not create a runtime component: ' + component.name
});
}
//throw new Error('Could not create a runtime component: ' + component.name);
}
if (parentEntity !== null && runtimeComponent) {
runtimeComponent.parentEntity = parentEntity;
}
);
}
}
if (runtimeComponent) {
loaded.push(runtimeComponent);
}
if (includeDependencies && runtimeComponent) {
/**
* Before we announce the creation of this component, we should get
* a list of all dependencies of this component, because once we announce
* the creation of this component - the linking system will attempt to resolve
* all dependencies
*/
var dependencies = runtimeComponent.getDependencies();
/**
* Now - we should systematically check if we have the dependency already
* loaded (in our runtime environment) - if we have - we just ignore loading this dependency (for now)
*
* We don't override runtime versions of the same component in the database because the user
* could be working with it and it should be the latest version.
*/
dependencies = dependencies.reduce(
function (result, dependency) {
if (GameLib.EntityManager.Instance.findComponentById(dependency)) {
/**
* Don't add the dependency
*/
} else {
result.push(dependency);
}
return result;
},
[]
);
/**
* Also check if we did not already load this component quite recently
*/
dependencies = dependencies.reduce(
function (result, dependency) {
var found = loaded.reduce(
function (result, component) {
if (component.id === dependency) {
result = true;
}
return result;
},
false
);
if (!found) {
result.push(dependency);
}
return result;
},
[]
);
/**
* We should now check our 'loading' list and add all dependencies which are not already in there
*/
dependencies.map(
function (dependency) {
if (loading.indexOf(dependency) === -1) {
loading.push(dependency);
}
}
)
}
/**
* Ok - now we have a super good idea of which components still need to load -
* they live in the 'loading' list.
*
* At this point - the runtime components are created, but they are not ready
* to be used. They may have dependencies to other components, which still need
* to load, or may never be loaded.
*
* It is however safe, to announce, that we created the
* runtime version of it, however it could still have some dependencies.
*
* The Linking system will then kick in and try to resolve all dependencies
*/
if (runtimeComponent && onComponentLoaded) {
onComponentLoaded(runtimeComponent);
}
if (runtimeComponent) {
GameLib.Event.Emit(
GameLib.Event.COMPONENT_CREATED,
{
component: runtimeComponent
}
);
__system.loaded.push(runtimeComponent.id);
if (includeDependencies) {
/**
* Before we announce the creation of this component, we should get
* a list of all dependencies of this component, because once we announce
* the creation of this component - the linking system will attempt to resolve
* all dependencies
*/
var dependencies = runtimeComponent.getDependencies();
__system.otherDependencies.map(
function(id) {
var index = dependencies.indexOf(id);
if (index !== -1) {
dependencies.splice(index, 1);
}
}
);
dependencies.map(
function(id) {
GameLib.Utils.PushUnique(this.otherDependencies, id);
}.bind(__system)
);
/**
* Don't try to download failed components again
*/
dependencies = dependencies.reduce(
function(result, id) {
if (__system.failed.indexOf(id) === -1) {
result.push(id);
} else {
console.log('ignoring failed component : ' + id);
}
return result;
}.bind(__system),
[]
);
/**
* Now - we should systematically check if we have the dependency already
* loaded (in our runtime environment) - if we have - we just ignore loading this dependency (for now)
*
* We don't override runtime versions of the same component in the database because the user
* could be working with it and it should be the latest version.
*/
dependencies = dependencies.reduce(
function (result, dependency) {
if (GameLib.EntityManager.Instance.findComponentById(dependency)) {
/**
* Don't add the dependency
*/
} else {
result.push(dependency);
}
return result;
},
[]
);
/**
* Also check if this dependency is not already in our loaded
*/
dependencies = dependencies.reduce(
function (result, dependency) {
if (__system.loaded.indexOf(dependency) === -1) {
result.push(dependency);
}
return result;
},
[]
);
/**
* We should now check our 'loading' list and add all dependencies which are not already in there
*/
dependencies.map(
function (dependency) {
GameLib.Utils.PushUnique(__system.loading, dependency);
}
);
__system.loadComponent(apiUrl, dependencies, includeDependencies, clientCallback, clientErrorCallback);
GameLib.Event.Emit(
GameLib.Event.LOAD_PROGRESS,
{
loaded : __system.loaded.length,
toProcess : dependencies.length
}
);
}
// GameLib.Event.Emit(
// GameLib.Event.COMPONENT_DOWNLOAD_COMPLETE,
// {
// loaded: __system.loaded
// }
// );
if (__system.onComponentLoaded) {
__system.onComponentLoaded(runtimeComponent);
}
}
var toProcess = GameLib.Utils.Difference(loaded.map(function(component){return component.id}), loading);
}(this);
if (!runtimeComponent) {
/**
* Make sure we don't try to download this component again, it failed
*/
var index = toProcess.indexOf(component.id);
xhr.onprogress = function(__id) {
return function (progressEvent) {
if (index !== -1) {
toProcess.splice(index, 1);
var progress = 0;
if (progressEvent.total !== 0) {
progress = Math.round(Number(progressEvent.loaded / progressEvent.total) * 100);
}
}
GameLib.Event.Emit(
GameLib.Event.LOAD_PROGRESS,
{
loaded : loaded.length,
toProcess : toProcess.length
if (this.onComponentProgress) {
this.onComponentProgress(__id, progress)
}
);
}.bind(this);
}(id);
if (toProcess.length === 0) {
xhr.onerror = function(__id) {
return function (error) {
console.warn('component load failed for component ID ' + __id);
if (clientCallback) {
clientCallback({
components : loaded
if (this.onComponentError) {
this.onComponentError(__id, error)
}
if (clientErrorCallback) {
clientErrorCallback({
message : 'xhr request failure'
})
}
}.bind(this);
}(id);
GameLib.Event.Emit(
GameLib.Event.COMPONENT_DOWNLOAD_COMPLETE,
{
loaded: loaded
}
)
} else {
download.bind(__system)(toProcess.pop(), parentEntity);
}
xhr.open(
'GET',
apiUrl + '/component/load/' + id
);
};
}(this);
xhr.send();
xhr.onprogress = function(__id) {
return function (progressEvent) {
}.bind(this)
var progress = 0;
);
if (progressEvent.total !== 0) {
progress = Math.round(Number(progressEvent.loaded / progressEvent.total) * 100);
}
if (onComponentProgress) {
onComponentProgress(__id, progress)
}
};
}(id);
xhr.onerror = function(__id) {
return function (error) {
console.warn('component load failed for component ID ' + __id);
if (onComponentError) {
onComponentError(__id, error)
}
if (clientErrorCallback) {
clientErrorCallback({
message : 'xhr request failure'
})
}
};
}(id);
xhr.open(
'GET',
apiUrl + '/component/load/' + id
);
xhr.send();
}.bind(this);
};
@ -689,7 +681,7 @@ GameLib.System.Storage.prototype.load = function(data, clientCallback, clientErr
data.includeDependencies,
clientCallback,
clientErrorCallback
)(data.ids[0], null);
);
} else {
console.log('No components selected');
}