aotrautils-srv 0.0.1841 → 0.0.1847
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aotrautils-srv/aotrautils-srv.build.js +1441 -371
- aotrautils-srv/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (
|
|
3
|
+
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (02/06/2026-01:08:18)»*/
|
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
|
5
5
|
|
|
6
6
|
|
|
@@ -35,7 +35,7 @@ PERFORM_TESTS_ON_LIBRARY=false;
|
|
|
35
35
|
|
|
36
36
|
const ROOT_UUID="00000000-0000-0000-0000-000000000000";
|
|
37
37
|
const DEFAULT_UUID_ATTR_NAME="JSONid";/* Yet Another UUID */
|
|
38
|
-
const DEFAULT_CLASSNAME_ATTR_NAME="JSONtype";/* Yet Another
|
|
38
|
+
const DEFAULT_CLASSNAME_ATTR_NAME="JSONtype";/* Yet Another ClassName */
|
|
39
39
|
const DEFAULT_POINTER_TO_ATTR_NAME="JSONref";/* Yet Another PointerTo */
|
|
40
40
|
|
|
41
41
|
//=========================================================================
|
|
@@ -1760,7 +1760,7 @@ function encodeXORNoPattern(strToEncode,key){
|
|
|
1760
1760
|
|
|
1761
1761
|
// CAUTION : This method is declared this way because it's also used in a nodejs context!
|
|
1762
1762
|
// TODO : FIXME : DO THIS FOR ALL OF THESE COMMON METHODS AND FUNCTIONS !
|
|
1763
|
-
generateRandomString=function(length,/*NULLABLE*/mode=null){
|
|
1763
|
+
generateRandomString=function(length,/*NULLABLE*/mode=null, separator="~>"){
|
|
1764
1764
|
|
|
1765
1765
|
// This list must be very conservative, because we want to be able to pass it through GET URLs !
|
|
1766
1766
|
// OLD (not enough conservative ?) : const ALLOWED_CHARS="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0912346789_-£¢¤¬²³¼½¾";
|
|
@@ -1768,7 +1768,7 @@ generateRandomString=function(length,/*NULLABLE*/mode=null){
|
|
|
1768
1768
|
|
|
1769
1769
|
|
|
1770
1770
|
if(typeof(length)==="string"){
|
|
1771
|
-
if(contains(length,"
|
|
1771
|
+
if(contains(length,"~>")){
|
|
1772
1772
|
length=Math.getRandomInRange(length);
|
|
1773
1773
|
}else{
|
|
1774
1774
|
length=64;
|
|
@@ -2049,7 +2049,7 @@ window.nothing=function(nullable, insist=false){
|
|
|
2049
2049
|
}
|
|
2050
2050
|
|
|
2051
2051
|
window.getOrCreateEmptyAttribute=function(parentObject, attributeNameOrAttributesNames,
|
|
2052
|
-
// BECAUSE CAUTION : if we have defaultValue as parameter, sometimes because of the recursive call, it can refer to a persisting object ! Thus, creating a strange bug.
|
|
2052
|
+
// BECAUSE CAUTION : We don't allow to specify a default initialization value as parameter, because if we have defaultValue as parameter, sometimes because of the recursive call, it can refer to a persisting object ! Thus, creating a strange bug.
|
|
2053
2053
|
// (SO WE ALWAYS NEED TO START AS A BLANK STATE FOR DEFAULT VALUE !)
|
|
2054
2054
|
forceArrayAttributeDefaultValue=false){
|
|
2055
2055
|
if(nothing(parentObject)) return null;
|
|
@@ -2085,6 +2085,7 @@ window.getOrCreateEmptyAttribute=function(parentObject, attributeNameOrAttribute
|
|
|
2085
2085
|
return null; // (case no child was found or it could be not created.)
|
|
2086
2086
|
}
|
|
2087
2087
|
|
|
2088
|
+
// ( Equivalent to syntax: value??defaultValue )
|
|
2088
2089
|
window.nonull=function(value,defaultValue){
|
|
2089
2090
|
if(value===false) return value;
|
|
2090
2091
|
if(nothing(value)) return defaultValue;
|
|
@@ -2153,7 +2154,7 @@ window.pushInArrayAsQueue=function(array, maxSize, item){
|
|
|
2153
2154
|
/*KNOWN LIMITATIONS : You will have an incomplete browsing loop if there are the string values "break" or "continue" in your array or associative array !*/
|
|
2154
2155
|
// (Alternatively, you can use this instead : for (const key of Object.keys(obj)) { const item=obj[key]; ... })
|
|
2155
2156
|
// CAUTION : Only use «return foreach» syntax in client code loops with SINGLE-LEVEL nested loops ONLY !
|
|
2156
|
-
window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
2157
|
+
window.foreach=function(arrayOfValues, doOnEachFunction,
|
|
2157
2158
|
/*OPTIONAL*/filterFunction=null,
|
|
2158
2159
|
// CAUTION ! Javascript sort compare function result is the same as in Java :
|
|
2159
2160
|
// 0: if(a==b)
|
|
@@ -3693,7 +3694,7 @@ Math.coerceInRange=window.aotest({
|
|
|
3693
3694
|
}, !PERFORM_TESTS_ON_LIBRARY);
|
|
3694
3695
|
|
|
3695
3696
|
|
|
3696
|
-
Math.getMinMax=function(strMinMaxRange, separator="
|
|
3697
|
+
Math.getMinMax=function(strMinMaxRange, separator="¬>"){
|
|
3697
3698
|
if(strMinMaxRange && isString(strMinMaxRange) && contains(strMinMaxRange,separator)){
|
|
3698
3699
|
let splits=strMinMaxRange.split(separator);
|
|
3699
3700
|
let min=parseInt(splits[0]);
|
|
@@ -3704,7 +3705,7 @@ Math.getMinMax=function(strMinMaxRange, separator="->"){
|
|
|
3704
3705
|
}
|
|
3705
3706
|
|
|
3706
3707
|
|
|
3707
|
-
Math.isInMinMax=function(minMaxRange,value,mode="][",separator="
|
|
3708
|
+
Math.isInMinMax=function(minMaxRange,value,mode="][",separator="¬>"){
|
|
3708
3709
|
let minMax=null;
|
|
3709
3710
|
|
|
3710
3711
|
if(minMaxRange){
|
|
@@ -3727,7 +3728,7 @@ Math.isInMinMax=function(minMaxRange,value,mode="][",separator="->"){
|
|
|
3727
3728
|
}
|
|
3728
3729
|
|
|
3729
3730
|
|
|
3730
|
-
Math.getRandomInRange=function(str, separator="
|
|
3731
|
+
Math.getRandomInRange=function(str, separator="~>"){
|
|
3731
3732
|
if(str && isString(str) && contains(str,separator)){
|
|
3732
3733
|
let splits=Math.getMinMax(str,separator);
|
|
3733
3734
|
let minValue=splits.min;
|
|
@@ -3912,7 +3913,10 @@ window.isBoolean=function(b){
|
|
|
3912
3913
|
return typeof b === "boolean";
|
|
3913
3914
|
}
|
|
3914
3915
|
window.isFunction=function(functionToCheck){
|
|
3915
|
-
return (functionToCheck && {}.toString.call(functionToCheck) === "[object Function]");
|
|
3916
|
+
return (functionToCheck && (isAsyncFunction(functionToCheck) || {}.toString.call(functionToCheck) === "[object Function]"));
|
|
3917
|
+
}
|
|
3918
|
+
window.isAsyncFunction=function(functionToCheck){
|
|
3919
|
+
return (functionToCheck && functionToCheck.constructor && functionToCheck.constructor.name==="AsyncFunction");
|
|
3916
3920
|
}
|
|
3917
3921
|
window.isPrimitive=function(p){
|
|
3918
3922
|
return (isNumber(p) || isString(p) || isBoolean(p));
|
|
@@ -3920,6 +3924,8 @@ window.isPrimitive=function(p){
|
|
|
3920
3924
|
window.isObject=function(o){ // (includes also plain objects)
|
|
3921
3925
|
return !isFunction(o) && !isPrimitive(o);
|
|
3922
3926
|
}
|
|
3927
|
+
|
|
3928
|
+
|
|
3923
3929
|
// NO : DOES NOT WORK !!!
|
|
3924
3930
|
//window.isPlainObject=function(o){
|
|
3925
3931
|
// return Object.prototype.toString.call(o)==="[object Object]";
|
|
@@ -3928,9 +3934,19 @@ window.isClassObject=function(o){
|
|
|
3928
3934
|
return isObject(o) && !isNativeClass(o);
|
|
3929
3935
|
}
|
|
3930
3936
|
window.getClassName=function(obj){
|
|
3931
|
-
if(
|
|
3937
|
+
if((typeof obj === "undefined") || obj==null) return null;
|
|
3932
3938
|
if(!obj.constructor) return null;
|
|
3933
|
-
|
|
3939
|
+
//if(!empty(Object.getPrototypeOf(obj).constructor.name))
|
|
3940
|
+
// return Object.getPrototypeOf(obj).constructor.name;
|
|
3941
|
+
if(!empty(obj.constructor.name)){
|
|
3942
|
+
return obj.constructor.name;
|
|
3943
|
+
}else{
|
|
3944
|
+
//TRACE
|
|
3945
|
+
lognow("WARN : Object is an instance of an anonymous class ! Cannot infer its class name ! Please fix its class definition ! obj:", obj);
|
|
3946
|
+
}
|
|
3947
|
+
// WORKAROUND
|
|
3948
|
+
if(!empty(obj[CLASSNAME_ATTRIBUTE_NAME])) return obj[CLASSNAME_ATTRIBUTE_NAME];
|
|
3949
|
+
return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1];
|
|
3934
3950
|
}
|
|
3935
3951
|
window.isNativeClass=function(obj){
|
|
3936
3952
|
let className=getClassName(obj);
|
|
@@ -4097,8 +4113,12 @@ getUUID=function(mode=null){
|
|
|
4097
4113
|
}
|
|
4098
4114
|
|
|
4099
4115
|
|
|
4116
|
+
|
|
4117
|
+
window.CLASSNAME_ATTRIBUTE_NAME="__className";
|
|
4118
|
+
|
|
4119
|
+
|
|
4100
4120
|
// JSON
|
|
4101
|
-
window.
|
|
4121
|
+
window.instantiate=function(className=null){
|
|
4102
4122
|
let newObj={};
|
|
4103
4123
|
|
|
4104
4124
|
if(!className || !isString(className)) return newObj;
|
|
@@ -4116,7 +4136,9 @@ window.instanciate=function(className=null){
|
|
|
4116
4136
|
// If class name is a simple array, a simple «[]» is enough :
|
|
4117
4137
|
if(className==="Array") return [];
|
|
4118
4138
|
|
|
4139
|
+
|
|
4119
4140
|
|
|
4141
|
+
|
|
4120
4142
|
let classInWindow=globalThis[className];
|
|
4121
4143
|
if(typeof(classInWindow)=="undefined"){
|
|
4122
4144
|
// TRACE
|
|
@@ -4130,20 +4152,26 @@ window.instanciate=function(className=null){
|
|
|
4130
4152
|
classInWindow=global[className];
|
|
4131
4153
|
if(!classInWindow){
|
|
4132
4154
|
// TRACE
|
|
4133
|
-
lognow(`ERROR : CAUTION : «${className}» class does not seem to be registered in the global object. Cannot
|
|
4155
|
+
lognow(`ERROR : CAUTION : «${className}» class does not seem to be registered in the global object. Cannot instantiate it.`);
|
|
4134
4156
|
|
|
4157
|
+
// DEBUG
|
|
4135
4158
|
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4136
4159
|
newObj=eval("new "+className+"();");
|
|
4137
4160
|
|
|
4138
4161
|
}
|
|
4139
4162
|
}else{
|
|
4140
|
-
//
|
|
4141
|
-
lognow(`ERROR : CAUTION : «${className}» class does not seem to be accessible in the globalThis nor window object, and global object does not exist. Cannot
|
|
4163
|
+
// TRACEfireNewPuff()
|
|
4164
|
+
lognow(`ERROR : CAUTION : «${className}» class does not seem to be accessible in the globalThis nor window object, and global object does not exist. Cannot instantiate it.`);
|
|
4142
4165
|
|
|
4166
|
+
// DEBUG
|
|
4143
4167
|
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4144
4168
|
newObj=eval("new "+className+"();");
|
|
4145
4169
|
|
|
4146
4170
|
}
|
|
4171
|
+
|
|
4172
|
+
// WORKAROUND
|
|
4173
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4174
|
+
|
|
4147
4175
|
return newObj;
|
|
4148
4176
|
}
|
|
4149
4177
|
}
|
|
@@ -4154,21 +4182,21 @@ window.instanciate=function(className=null){
|
|
|
4154
4182
|
// newObj=Reflect.construct(classInWindow);
|
|
4155
4183
|
// }catch(e1){
|
|
4156
4184
|
//// // TRACE
|
|
4157
|
-
//// console.log("WARN : Could not
|
|
4185
|
+
//// console.log("WARN : Could not instantiate class «"+className+"». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4158
4186
|
// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4159
4187
|
// newObj=eval("new "+className+"();");
|
|
4160
4188
|
// }
|
|
4161
4189
|
// }catch(e2){
|
|
4162
4190
|
//// // TRACE
|
|
4163
|
-
//// console.log("ERROR : Could not
|
|
4191
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with eval. (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4164
4192
|
// try{
|
|
4165
4193
|
//// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4166
4194
|
//// newObj=eval("new window."+className+"();");
|
|
4167
4195
|
// newObj=Reflect.construct(classInWindow);
|
|
4168
4196
|
// }catch(e3){
|
|
4169
4197
|
//// // TRACE
|
|
4170
|
-
//// console.log("ERROR : Could not
|
|
4171
|
-
//// console.log("ERROR : Could not
|
|
4198
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with eval and «window.». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4199
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with Reflect and «window.». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4172
4200
|
// // TODO : FIXME : This is the most performance-costing fallback :
|
|
4173
4201
|
// try{
|
|
4174
4202
|
// if(classInWindow){
|
|
@@ -4180,7 +4208,7 @@ window.instanciate=function(className=null){
|
|
|
4180
4208
|
// }
|
|
4181
4209
|
// }catch(e4){
|
|
4182
4210
|
//// // TRACE
|
|
4183
|
-
//// console.log("ERROR : Could not
|
|
4211
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with prototype affectation.");
|
|
4184
4212
|
//// console.log("ERROR : Returning plain object since all instantiation methods have failed for class «"+className+"».");
|
|
4185
4213
|
// }
|
|
4186
4214
|
// }
|
|
@@ -4188,17 +4216,17 @@ window.instanciate=function(className=null){
|
|
|
4188
4216
|
|
|
4189
4217
|
// NEW :
|
|
4190
4218
|
newObj=new classInWindow();
|
|
4219
|
+
|
|
4220
|
+
// WORKAROUND
|
|
4221
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4222
|
+
|
|
4191
4223
|
return newObj;
|
|
4192
4224
|
};
|
|
4193
4225
|
|
|
4194
4226
|
|
|
4195
4227
|
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
4228
|
JSON.stringifyDecycle=function(obj){
|
|
4201
|
-
let decycled=JSON.decycle(obj,null,
|
|
4229
|
+
let decycled=JSON.decycle(obj,null,CLASSNAME_ATTRIBUTE_NAME);
|
|
4202
4230
|
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
4203
4231
|
return stringifyObject(decycled);
|
|
4204
4232
|
}
|
|
@@ -4206,6 +4234,8 @@ JSON.stringifyDecycle=function(obj){
|
|
|
4206
4234
|
|
|
4207
4235
|
JSON.parseRecycle=function(str){
|
|
4208
4236
|
|
|
4237
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
4238
|
+
|
|
4209
4239
|
const parsedDecycled=parseJSON(str);
|
|
4210
4240
|
let restoreClass=function(objParam){
|
|
4211
4241
|
|
|
@@ -4215,11 +4245,11 @@ JSON.parseRecycle=function(str){
|
|
|
4215
4245
|
if(isArray(objParam)){
|
|
4216
4246
|
newObj=[];
|
|
4217
4247
|
}else{
|
|
4218
|
-
let className=objParam[
|
|
4248
|
+
let className=objParam[attributeClassName];
|
|
4219
4249
|
if(!className || isNativeClassName(className)){
|
|
4220
4250
|
newObj= {};
|
|
4221
4251
|
}else{
|
|
4222
|
-
newObj=
|
|
4252
|
+
newObj=instantiate(className);
|
|
4223
4253
|
}
|
|
4224
4254
|
}
|
|
4225
4255
|
|
|
@@ -4249,8 +4279,11 @@ JSON.parseRecycle=function(str){
|
|
|
4249
4279
|
,(itemOrAttr)=>{ return !isFunction(itemOrAttr); }
|
|
4250
4280
|
);
|
|
4251
4281
|
|
|
4252
|
-
|
|
4253
|
-
|
|
4282
|
+
// However we have to keep the class information attribute for further house !
|
|
4283
|
+
if(destination[attributeClassName]
|
|
4284
|
+
//...except if the object is a simple object :
|
|
4285
|
+
&& destination[attributeClassName]=="Object"){
|
|
4286
|
+
delete destination[attributeClassName];
|
|
4254
4287
|
}
|
|
4255
4288
|
|
|
4256
4289
|
};
|
|
@@ -4268,6 +4301,7 @@ JSON.parseRecycle=function(str){
|
|
|
4268
4301
|
|
|
4269
4302
|
|
|
4270
4303
|
window.clone=function(obj){
|
|
4304
|
+
if(obj==null) return null;
|
|
4271
4305
|
// OLD :
|
|
4272
4306
|
const str=JSON.stringifyDecycle(obj);
|
|
4273
4307
|
const newObj=JSON.parseRecycle(str);
|
|
@@ -4740,7 +4774,7 @@ window.isFlatMap=function(obj){
|
|
|
4740
4774
|
|
|
4741
4775
|
window.getAsFlatStructure=function(rawObject
|
|
4742
4776
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4743
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4777
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4744
4778
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4745
4779
|
){
|
|
4746
4780
|
// Flattening :
|
|
@@ -4791,7 +4825,7 @@ function flattenGraph(root, UUID_ATTR_NAME, CLASSNAME_ATTR_NAME, POINTER_TO_ATTR
|
|
|
4791
4825
|
|
|
4792
4826
|
window.getAsTreeStructure=function(oldMap, keepClassName=false
|
|
4793
4827
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4794
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4828
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4795
4829
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4796
4830
|
){
|
|
4797
4831
|
|
|
@@ -4813,7 +4847,7 @@ window.restoreGraph=(flatData, keepClassName=false, UUID_ATTR_NAME, CLASSNAME_AT
|
|
|
4813
4847
|
|
|
4814
4848
|
const objData = flatData[id];
|
|
4815
4849
|
const className = objData[CLASSNAME_ATTR_NAME];
|
|
4816
|
-
const newInstance =
|
|
4850
|
+
const newInstance = instantiate(className);
|
|
4817
4851
|
|
|
4818
4852
|
restoredObjects[id]=newInstance; // Assign ID early to handle circular references
|
|
4819
4853
|
|
|
@@ -5178,7 +5212,7 @@ window.getMonoThreadedTimeout=function(actuator=null){
|
|
|
5178
5212
|
|
|
5179
5213
|
// ======================== Routine ========================
|
|
5180
5214
|
|
|
5181
|
-
window.MonoThreadedRoutine=class {
|
|
5215
|
+
window.MonoThreadedRoutine=class MonoThreadedRoutine{
|
|
5182
5216
|
constructor(actuator,refreshMillis=null,startDependsOnParentOnly=false){
|
|
5183
5217
|
|
|
5184
5218
|
this.actuator=actuator;
|
|
@@ -5188,15 +5222,21 @@ window.MonoThreadedRoutine=class {
|
|
|
5188
5222
|
this.time=getNow();
|
|
5189
5223
|
this.refreshMillis=refreshMillis;
|
|
5190
5224
|
|
|
5191
|
-
this.
|
|
5225
|
+
this.managedTimeFactor=1;
|
|
5192
5226
|
// Three-states : started, paused, stopped.
|
|
5193
5227
|
|
|
5194
5228
|
}
|
|
5195
5229
|
|
|
5196
|
-
|
|
5197
|
-
this
|
|
5230
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5231
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5198
5232
|
return this;
|
|
5199
5233
|
}
|
|
5234
|
+
|
|
5235
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5236
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5237
|
+
return this;
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5200
5240
|
isStarted(){
|
|
5201
5241
|
return this.started || this.startDependsOnParentOnly;
|
|
5202
5242
|
}
|
|
@@ -5233,7 +5273,7 @@ window.MonoThreadedRoutine=class {
|
|
|
5233
5273
|
if(!this.isStarted() || this.paused) return;
|
|
5234
5274
|
// Looping index with a delay :
|
|
5235
5275
|
|
|
5236
|
-
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5276
|
+
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor);
|
|
5237
5277
|
if(!this.refreshMillis || delayHasPassed){
|
|
5238
5278
|
if(this.refreshMillis && delayHasPassed){
|
|
5239
5279
|
this.time=getNow();
|
|
@@ -5243,7 +5283,8 @@ window.MonoThreadedRoutine=class {
|
|
|
5243
5283
|
this.stop(args);
|
|
5244
5284
|
return;
|
|
5245
5285
|
}
|
|
5246
|
-
if(this.actuator.doOnEachStep)
|
|
5286
|
+
if(this.actuator.doOnEachStep)
|
|
5287
|
+
this.actuator.doOnEachStep(args);
|
|
5247
5288
|
}
|
|
5248
5289
|
|
|
5249
5290
|
}
|
|
@@ -5272,19 +5313,27 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5272
5313
|
this.time=getNow();
|
|
5273
5314
|
this.refreshMillis=refreshMillis;
|
|
5274
5315
|
|
|
5275
|
-
this.
|
|
5316
|
+
this.managedTimeFactor=1;
|
|
5317
|
+
|
|
5276
5318
|
|
|
5277
5319
|
// Three-states : started, paused, stopped.
|
|
5278
5320
|
}
|
|
5279
|
-
|
|
5280
|
-
|
|
5321
|
+
|
|
5322
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5323
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5324
|
+
return this;
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5328
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5281
5329
|
|
|
5282
5330
|
// We recalculate total time-dependent values :
|
|
5283
|
-
this.totalTimeMillis=this.totalTimeMillis*this.
|
|
5331
|
+
this.totalTimeMillis=this.totalTimeMillis*this.managedTimeFactor;
|
|
5284
5332
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.actualValue))/this.totalTimeMillis);
|
|
5285
5333
|
|
|
5286
5334
|
return this;
|
|
5287
5335
|
}
|
|
5336
|
+
|
|
5288
5337
|
isStarted(){
|
|
5289
5338
|
return this.started || this.startDependsOnParentOnly;
|
|
5290
5339
|
}
|
|
@@ -5296,7 +5345,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5296
5345
|
setNewGoal(goalValue, refreshMillisParam=null, totalTimeMillis=null){
|
|
5297
5346
|
if(Math.round(this.value)===Math.round(goalValue)) return;
|
|
5298
5347
|
if(refreshMillisParam) this.refreshMillis=refreshMillisParam;
|
|
5299
|
-
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.
|
|
5348
|
+
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.managedTimeFactor;
|
|
5300
5349
|
|
|
5301
5350
|
this.goalValue=goalValue;
|
|
5302
5351
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.value))/this.totalTimeMillis);
|
|
@@ -5383,7 +5432,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5383
5432
|
}
|
|
5384
5433
|
|
|
5385
5434
|
hasDelayPassed(){
|
|
5386
|
-
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5435
|
+
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor));
|
|
5387
5436
|
}
|
|
5388
5437
|
|
|
5389
5438
|
|
|
@@ -5409,7 +5458,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5409
5458
|
|
|
5410
5459
|
|
|
5411
5460
|
|
|
5412
|
-
/*utils AI library associated with aotra version : «1_29072022-2359 (
|
|
5461
|
+
/*utils AI library associated with aotra version : «1_29072022-2359 (02/06/2026-01:08:18)»*/
|
|
5413
5462
|
/*-----------------------------------------------------------------------------*/
|
|
5414
5463
|
|
|
5415
5464
|
|
|
@@ -5555,11 +5604,383 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
5555
5604
|
|
|
5556
5605
|
|
|
5557
5606
|
|
|
5558
|
-
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (
|
|
5607
|
+
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (02/06/2026-01:08:18)»*/
|
|
5559
5608
|
/*-----------------------------------------------------------------------------*/
|
|
5560
5609
|
|
|
5561
5610
|
|
|
5562
|
-
/* ## Utility methods in a javascript, for AORTAC subsystem (
|
|
5611
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (client)
|
|
5612
|
+
*
|
|
5613
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
5614
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
5615
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
5616
|
+
*
|
|
5617
|
+
* # Library name : «aotrautils»
|
|
5618
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
5619
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
5620
|
+
* # Author email : info@alqemia.com
|
|
5621
|
+
* # Organization name : Alqemia
|
|
5622
|
+
* # Organization email : admin@alqemia.com
|
|
5623
|
+
* # Organization website : https://alqemia.com
|
|
5624
|
+
*
|
|
5625
|
+
*
|
|
5626
|
+
*/
|
|
5627
|
+
|
|
5628
|
+
|
|
5629
|
+
|
|
5630
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
5631
|
+
if(typeof(window)==="undefined") window=global;
|
|
5632
|
+
|
|
5633
|
+
|
|
5634
|
+
|
|
5635
|
+
|
|
5636
|
+
// ==================================================================================================================
|
|
5637
|
+
|
|
5638
|
+
|
|
5639
|
+
// AORTAC CLIENT
|
|
5640
|
+
|
|
5641
|
+
|
|
5642
|
+
|
|
5643
|
+
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
5644
|
+
|
|
5645
|
+
const AORTAC_CLIENT_FORCE_SSL_USAGE=true;
|
|
5646
|
+
|
|
5647
|
+
// New implementation :
|
|
5648
|
+
|
|
5649
|
+
class AORTACClientCell{
|
|
5650
|
+
|
|
5651
|
+
constructor(serverCellOrigin, model, view, isNodeContext=false){
|
|
5652
|
+
|
|
5653
|
+
this.clientId=getUUID();
|
|
5654
|
+
|
|
5655
|
+
this.model=model;
|
|
5656
|
+
this.view=view;
|
|
5657
|
+
this.isNodeContext=isNodeContext;
|
|
5658
|
+
|
|
5659
|
+
const infos=splitURL(serverCellOrigin);
|
|
5660
|
+
|
|
5661
|
+
this.protocol=nonull(infos.protocol,"ws");
|
|
5662
|
+
this.host=nonull(infos.host,"localhost");
|
|
5663
|
+
this.port=nonull(infos.port,"30000");
|
|
5664
|
+
this.usesSSL=AORTAC_CLIENT_FORCE_SSL_USAGE;
|
|
5665
|
+
|
|
5666
|
+
this.startTime=null;
|
|
5667
|
+
|
|
5668
|
+
this.initialClientInstance=null;
|
|
5669
|
+
this.clientInstances={};
|
|
5670
|
+
|
|
5671
|
+
|
|
5672
|
+
|
|
5673
|
+
this.ready=false;
|
|
5674
|
+
|
|
5675
|
+
this.lobuleZone=new ClientLobule();
|
|
5676
|
+
|
|
5677
|
+
|
|
5678
|
+
}
|
|
5679
|
+
|
|
5680
|
+
|
|
5681
|
+
start(manifestationZone){
|
|
5682
|
+
|
|
5683
|
+
const self=this;
|
|
5684
|
+
|
|
5685
|
+
|
|
5686
|
+
this.manifestationZone=manifestationZone;
|
|
5687
|
+
this.startTime=getNow();
|
|
5688
|
+
|
|
5689
|
+
|
|
5690
|
+
|
|
5691
|
+
return new Promise((resolve,reject)=>{
|
|
5692
|
+
|
|
5693
|
+
|
|
5694
|
+
// 1) Initial connection
|
|
5695
|
+
self.initialClientInstance=initClient(self.isNodeContext, false, (socketToServerClientInstance, initialClientInstanceLocal)=>{
|
|
5696
|
+
|
|
5697
|
+
// DBG
|
|
5698
|
+
lognow("DEBUG : Starting client...");
|
|
5699
|
+
|
|
5700
|
+
initialClientInstanceLocal.socketToServerClientInstance=socketToServerClientInstance;
|
|
5701
|
+
|
|
5702
|
+
// INITIATE CLIENT HELLO SEQUENCE
|
|
5703
|
+
self.initiateClientHelloSequence();
|
|
5704
|
+
|
|
5705
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
5706
|
+
self.handleClientHelloSequence(resolve);
|
|
5707
|
+
|
|
5708
|
+
|
|
5709
|
+
}, self.protocol+"://"+self.host, self.port, self.usesSSL);
|
|
5710
|
+
self.initialClientInstance.client.start();
|
|
5711
|
+
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
5714
|
+
|
|
5715
|
+
|
|
5716
|
+
// ===================================================================================================
|
|
5717
|
+
// SEQUENCES
|
|
5718
|
+
// ===================================================================================================
|
|
5719
|
+
|
|
5720
|
+
|
|
5721
|
+
// ==========================================================
|
|
5722
|
+
// CLIENT HELLO SEQUENCE
|
|
5723
|
+
|
|
5724
|
+
// 1) We send a client hello request
|
|
5725
|
+
// 2) Server cell receives it and asks the blob for all servers which this client must reconnect to
|
|
5726
|
+
// (also server sends its objects corresponding to zone, if it has some of them)
|
|
5727
|
+
// 3) We receive the confirmation and reconnect to all the indicated servers
|
|
5728
|
+
// 4) Each concerned server cell receives it and registers the client and sends the hello confirmation
|
|
5729
|
+
// (and also the objects in the zone it had stored when trying to know if it's a concerned server or not)
|
|
5730
|
+
// 5) We have all the objects, we can initialize the client model.
|
|
5731
|
+
|
|
5732
|
+
|
|
5733
|
+
/*private*/initiateClientHelloSequence(){
|
|
5734
|
+
// 1- We want to present ourselves to server cell :
|
|
5735
|
+
const clientHelloRequest={
|
|
5736
|
+
clientId:this.clientId,
|
|
5737
|
+
type:"clientHello",
|
|
5738
|
+
manifestationZone:this.manifestationZone
|
|
5739
|
+
};
|
|
5740
|
+
this.initialClientInstance.socketToServerClientInstance.send("protocol", clientHelloRequest);
|
|
5741
|
+
}
|
|
5742
|
+
|
|
5743
|
+
/*private*/handleClientHelloSequence(resolve){
|
|
5744
|
+
|
|
5745
|
+
const self=this;
|
|
5746
|
+
this.initialClientInstance.socketToServerClientInstance.receive("protocol", (messageParam, clientSocket)=>{
|
|
5747
|
+
|
|
5748
|
+
const message=JSON.recycle(messageParam);
|
|
5749
|
+
|
|
5750
|
+
const servers=message.servers;
|
|
5751
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
5752
|
+
const objects=message.objects;
|
|
5753
|
+
|
|
5754
|
+
lognow("DEBUG : Client received its servers :",servers);
|
|
5755
|
+
lognow("DEBUG : Client received some objects :",objects);
|
|
5756
|
+
|
|
5757
|
+
|
|
5758
|
+
self.lobuleZone.resetServersNumbers();
|
|
5759
|
+
self.lobuleZone.append(servers, "serversBag");
|
|
5760
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST ITS ROOTCONTAINER (during the «client hello» phase) !!!
|
|
5761
|
+
self.lobuleZone.append(objects);
|
|
5762
|
+
|
|
5763
|
+
const lobuleObjects=self.lobuleZone.getObjects();
|
|
5764
|
+
if(!servers || empty(servers)){
|
|
5765
|
+
// TRACE
|
|
5766
|
+
lognow("ERROR : Servers blob sent no servers to reconnect to. Aborting.");
|
|
5767
|
+
// (We only return the structural object, ie. the root container :)
|
|
5768
|
+
resolve(lobuleObjects);
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5772
|
+
if(empty(servers)){
|
|
5773
|
+
// TRACE
|
|
5774
|
+
lognow("ERROR : No servers to connect to. Aborting (not initiaing the reconnection sequence).");
|
|
5775
|
+
// (We only return the structural object, ie. the root container :)
|
|
5776
|
+
resolve(lobuleObjects);
|
|
5777
|
+
return;
|
|
5778
|
+
}
|
|
5779
|
+
|
|
5780
|
+
// 2) Re-connections :
|
|
5781
|
+
foreach(servers, serverOrigin=>{
|
|
5782
|
+
|
|
5783
|
+
const infos=splitURL(serverOrigin);
|
|
5784
|
+
|
|
5785
|
+
const protocol=nonull(infos.protocol,"ws");
|
|
5786
|
+
const host=nonull(infos.host,"localhost");
|
|
5787
|
+
const port=nonull(infos.port,"30000");
|
|
5788
|
+
const usesSSL=AORTAC_CLIENT_FORCE_SSL_USAGE;
|
|
5789
|
+
|
|
5790
|
+
const reconnectedClientInstance=initClient(self.isNodeContext, false, (socketToServerClientInstance, reconnectedClientInstanceLocal)=>{
|
|
5791
|
+
|
|
5792
|
+
// DBG
|
|
5793
|
+
lognow("DEBUG : Starting reconnected client...");
|
|
5794
|
+
|
|
5795
|
+
reconnectedClientInstanceLocal.socketToServerClientInstance=socketToServerClientInstance;
|
|
5796
|
+
|
|
5797
|
+
// INITIATE CLIENT RECONNECTED HELLO SEQUENCE
|
|
5798
|
+
self.initiateReconnectedClientHelloSequence(reconnectedClientInstance);
|
|
5799
|
+
|
|
5800
|
+
// HANDLE CLIENT RECONNECTED HELLO SEQUENCE
|
|
5801
|
+
self.handleReconnectedClientHelloSequence(reconnectedClientInstance, resolve)
|
|
5802
|
+
|
|
5803
|
+
|
|
5804
|
+
}, protocol+"://"+host, port, usesSSL);
|
|
5805
|
+
|
|
5806
|
+
self.clientInstances[serverOrigin]=reconnectedClientInstance;
|
|
5807
|
+
reconnectedClientInstance.client.start();
|
|
5808
|
+
});
|
|
5809
|
+
|
|
5810
|
+
|
|
5811
|
+
},{listenerMessageType:"clientHello.response"});
|
|
5812
|
+
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5815
|
+
// ----
|
|
5816
|
+
// RECONNECTED CLIENT HELLO SEQUENCE
|
|
5817
|
+
|
|
5818
|
+
|
|
5819
|
+
/*private*/initiateReconnectedClientHelloSequence(clientInstance){
|
|
5820
|
+
|
|
5821
|
+
// We want to present ourselves to server cell :
|
|
5822
|
+
const reconnectedClientHelloRequest={
|
|
5823
|
+
clientId:this.clientId,
|
|
5824
|
+
type:"reconnectedClientHello"
|
|
5825
|
+
};
|
|
5826
|
+
clientInstance.client.socketToServerClientInstance.send("protocol", reconnectedClientHelloRequest);
|
|
5827
|
+
|
|
5828
|
+
}
|
|
5829
|
+
|
|
5830
|
+
/*private*/handleReconnectedClientHelloSequence(clientInstance, resolve){
|
|
5831
|
+
|
|
5832
|
+
|
|
5833
|
+
const self=this;
|
|
5834
|
+
|
|
5835
|
+
clientInstance.client.socketToServerClientInstance.receive("protocol", (messageParam, clientSocket)=>{
|
|
5836
|
+
|
|
5837
|
+
const message=JSON.recycle(messageParam);
|
|
5838
|
+
|
|
5839
|
+
const objects=message.objects;
|
|
5840
|
+
|
|
5841
|
+
if(!objects || empty(objects)){
|
|
5842
|
+
// TRACE
|
|
5843
|
+
lognow("ERROR : This server cell sent no objects. Continuing.");
|
|
5844
|
+
|
|
5845
|
+
// DBG
|
|
5846
|
+
lognow("DEBUG : !!!!!!!!!!!!! message:",message);
|
|
5847
|
+
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5850
|
+
lognow("DEBUG : Client received some objects :",objects);
|
|
5851
|
+
|
|
5852
|
+
// We accumulate the received objects in the lobule zone :
|
|
5853
|
+
self.lobuleZone.append(objects, "objectsBag");
|
|
5854
|
+
|
|
5855
|
+
self.lobuleZone.incrementContactedServersNumber();
|
|
5856
|
+
|
|
5857
|
+
|
|
5858
|
+
// DBG
|
|
5859
|
+
lognow("DEBUG : !!!!!!!!!!!!! self.lobuleZone.getObjects():",self.lobuleZone.getObjects());
|
|
5860
|
+
|
|
5861
|
+
|
|
5862
|
+
if(self.lobuleZone.hasReceivedFromAllServers()){
|
|
5863
|
+
|
|
5864
|
+
const lobuleObjects=self.lobuleZone.getObjects();
|
|
5865
|
+
if(!lobuleObjects || empty(lobuleObjects)){
|
|
5866
|
+
// TRACE
|
|
5867
|
+
lognow("ERROR : No objects at all accumulated from server cells blob. Aborting.");
|
|
5868
|
+
resolve(null);
|
|
5869
|
+
return;
|
|
5870
|
+
}
|
|
5871
|
+
|
|
5872
|
+
// const allClientModelObjectsByClassName=self.sortByClassName(lobuleObjects);
|
|
5873
|
+
const allClientModelObjects=lobuleObjects;
|
|
5874
|
+
|
|
5875
|
+
//DBG
|
|
5876
|
+
// lognow("DEBUG : allClientModelObjectsByClassName : ",allClientModelObjectsByClassName);
|
|
5877
|
+
lognow("DEBUG : allClientModelObjects : ",allClientModelObjects);
|
|
5878
|
+
|
|
5879
|
+
if(empty(allClientModelObjects)){
|
|
5880
|
+
// TRACE
|
|
5881
|
+
lognow("ERROR : Servers blob sent no game level. Aborting.");
|
|
5882
|
+
resolve(allClientModelObjects);
|
|
5883
|
+
return;
|
|
5884
|
+
}
|
|
5885
|
+
|
|
5886
|
+
// DBG
|
|
5887
|
+
lognow("DEBUG : Client received its model objects:",message.objects);
|
|
5888
|
+
// lognow("DEBUG : Client received its model objects (after sorting):",allClientModelObjectsByClassName);
|
|
5889
|
+
|
|
5890
|
+
// DBG
|
|
5891
|
+
lognow("DEBUG : Client has received all its objects from the servers cells blob: ");
|
|
5892
|
+
|
|
5893
|
+
resolve(allClientModelObjects);
|
|
5894
|
+
}
|
|
5895
|
+
|
|
5896
|
+
|
|
5897
|
+
},{listenerMessageType:"reconnectedClientHello.response"});
|
|
5898
|
+
|
|
5899
|
+
}
|
|
5900
|
+
|
|
5901
|
+
|
|
5902
|
+
|
|
5903
|
+
// ================================================================================
|
|
5904
|
+
|
|
5905
|
+
|
|
5906
|
+
|
|
5907
|
+
|
|
5908
|
+
}
|
|
5909
|
+
|
|
5910
|
+
|
|
5911
|
+
|
|
5912
|
+
|
|
5913
|
+
class ClientLobule{
|
|
5914
|
+
|
|
5915
|
+
constructor(){
|
|
5916
|
+
this.serversBag=null;
|
|
5917
|
+
this.objectsBag=null;
|
|
5918
|
+
this.numberOfContactedServers=0;
|
|
5919
|
+
this.totalNumberOfServersToContact=0;
|
|
5920
|
+
this.clear();
|
|
5921
|
+
}
|
|
5922
|
+
append(objs, attributeName="objectsBag"){
|
|
5923
|
+
const self=this;
|
|
5924
|
+
if(!this[attributeName])
|
|
5925
|
+
this[attributeName]=[];
|
|
5926
|
+
foreach(objs,obj=>{
|
|
5927
|
+
self[attributeName].push(obj);
|
|
5928
|
+
},(obj)=>(!contains(this[attributeName], obj)));
|
|
5929
|
+
if(attributeName=="serversBag")
|
|
5930
|
+
this.totalNumberOfServersToContact+=objs.length;
|
|
5931
|
+
}
|
|
5932
|
+
getObjects(filterFunction=null){
|
|
5933
|
+
if(!filterFunction) return this.objectsBag;
|
|
5934
|
+
if(!this.objectsBag) return null;
|
|
5935
|
+
const results=[];
|
|
5936
|
+
foreach(this.objectsBag, (obj)=>{
|
|
5937
|
+
results.push(obj);
|
|
5938
|
+
},filterFunction);
|
|
5939
|
+
return results;
|
|
5940
|
+
}
|
|
5941
|
+
getServers(){
|
|
5942
|
+
if(!this.serversBag) return null;
|
|
5943
|
+
return this.serversBag;
|
|
5944
|
+
}
|
|
5945
|
+
clear(attributeName=null){
|
|
5946
|
+
if(!attributeName){
|
|
5947
|
+
this.serversBag=[];
|
|
5948
|
+
this.objectsBag=[];
|
|
5949
|
+
}else{
|
|
5950
|
+
this[attributeName]=[];
|
|
5951
|
+
}
|
|
5952
|
+
this.resetServersNumbers();
|
|
5953
|
+
return this;
|
|
5954
|
+
}
|
|
5955
|
+
resetServersNumbers(){
|
|
5956
|
+
this.numberOfContactedServers=0;
|
|
5957
|
+
this.totalNumberOfServersToContact=0;
|
|
5958
|
+
}
|
|
5959
|
+
incrementContactedServersNumber(increment=1){
|
|
5960
|
+
this.numberOfContactedServers+=increment;
|
|
5961
|
+
}
|
|
5962
|
+
hasReceivedFromAllServers(){
|
|
5963
|
+
return (this.totalNumberOfServersToContact<=this.numberOfContactedServers);
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
|
|
5967
|
+
|
|
5968
|
+
|
|
5969
|
+
|
|
5970
|
+
|
|
5971
|
+
|
|
5972
|
+
window.getAORTACClient=function(serverCellOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
5973
|
+
if(nothing(serverCellOrigin)){
|
|
5974
|
+
// TRACE
|
|
5975
|
+
lognow("ERROR : No known server node, cannot connect to servers cells blob. Aborting AORTAC client setup.");
|
|
5976
|
+
return null;
|
|
5977
|
+
}
|
|
5978
|
+
return new AORTACClientCell(serverCellOrigin, model, view, isNodeContext);
|
|
5979
|
+
}
|
|
5980
|
+
|
|
5981
|
+
|
|
5982
|
+
|
|
5983
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (server)
|
|
5563
5984
|
*
|
|
5564
5985
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
5565
5986
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -5592,8 +6013,8 @@ if(typeof(window)==="undefined") window=global;
|
|
|
5592
6013
|
|
|
5593
6014
|
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
|
5594
6015
|
|
|
5595
|
-
// New implementation :
|
|
5596
6016
|
AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS=2000;
|
|
6017
|
+
|
|
5597
6018
|
class AORTACServerCell{
|
|
5598
6019
|
|
|
5599
6020
|
constructor(quorumNumber=1, selfOrigin, outcomingCellsOrigins, model, controller, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
@@ -5606,14 +6027,14 @@ class AORTACServerCell{
|
|
|
5606
6027
|
const infos=splitURL(this.selfOrigin);
|
|
5607
6028
|
|
|
5608
6029
|
//DBG
|
|
5609
|
-
lognow("infos:::::::::::::",infos);
|
|
6030
|
+
lognow("(SERVER) infos:::::::::::::",infos);
|
|
5610
6031
|
|
|
5611
6032
|
this.protocol=nonull(infos.protocol,"ws");
|
|
5612
6033
|
this.host=nonull(infos.host,"localhost");
|
|
5613
6034
|
this.port=nonull(infos.port,"30000");
|
|
5614
6035
|
this.sslConfig=sslConfig;
|
|
5615
6036
|
|
|
5616
|
-
this.
|
|
6037
|
+
this.serverWrapper=null;
|
|
5617
6038
|
this.incomingCells={};
|
|
5618
6039
|
|
|
5619
6040
|
this.outcomingCellsOrigins=outcomingCellsOrigins;
|
|
@@ -5631,20 +6052,26 @@ class AORTACServerCell{
|
|
|
5631
6052
|
};
|
|
5632
6053
|
|
|
5633
6054
|
this.cellsOverview={};
|
|
6055
|
+
this.numberOfPartitions=null;
|
|
6056
|
+
this.partitionId=null;
|
|
6057
|
+
|
|
6058
|
+
this.isServerCellQuorumLastCell=false;
|
|
5634
6059
|
|
|
5635
6060
|
this.startTime=null;
|
|
5636
6061
|
|
|
5637
6062
|
this.quorumIsReachedSequenceIsInitiated=false;
|
|
5638
6063
|
// this.quorumCells=null; // (CAUTION : only the latest ready cell when quorum is reached has this attribute populated)
|
|
5639
6064
|
|
|
5640
|
-
this.
|
|
6065
|
+
this.lobuleZone=new ServerCellLobule();
|
|
6066
|
+
|
|
6067
|
+
this.clients={};
|
|
5641
6068
|
|
|
5642
6069
|
}
|
|
5643
6070
|
|
|
5644
6071
|
start(){
|
|
5645
6072
|
|
|
5646
6073
|
this.startTime=getNow();
|
|
5647
|
-
this.
|
|
6074
|
+
this.serverWrapper=this.launchServerForIncomingConnections();
|
|
5648
6075
|
|
|
5649
6076
|
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
5650
6077
|
// TRACE
|
|
@@ -5661,37 +6088,72 @@ class AORTACServerCell{
|
|
|
5661
6088
|
return this;
|
|
5662
6089
|
}
|
|
5663
6090
|
|
|
6091
|
+
|
|
6092
|
+
// ===================================================================================================
|
|
6093
|
+
// SERVER CELL CORE
|
|
6094
|
+
// ===================================================================================================
|
|
6095
|
+
|
|
5664
6096
|
/*private*/launchServerForIncomingConnections(){
|
|
5665
6097
|
const self=this;
|
|
5666
6098
|
|
|
5667
|
-
const
|
|
6099
|
+
const serverWrapper=initNodeServerInfrastructureWrapper(
|
|
5668
6100
|
// On each client connection :
|
|
5669
6101
|
null,
|
|
5670
6102
|
// On client finalization :
|
|
5671
6103
|
function(server){
|
|
5672
6104
|
|
|
6105
|
+
|
|
6106
|
+
const channelsNames=[];
|
|
6107
|
+
foreach(Object.keys(self.onReceiveMessageListeners["incoming"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
6108
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
6109
|
+
|
|
6110
|
+
|
|
5673
6111
|
// On-receive message listeners handling ;
|
|
5674
|
-
foreach(
|
|
5675
|
-
server.receive(channelName, (message, clientSocket)=>{
|
|
5676
|
-
self.executeListeners("incoming",channelName, message, clientSocket);
|
|
5677
|
-
});
|
|
5678
|
-
});
|
|
5679
|
-
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{
|
|
6112
|
+
foreach(channelsNames, channelName=>{
|
|
5680
6113
|
server.receive(channelName, (message, clientSocket)=>{
|
|
6114
|
+
|
|
6115
|
+
// DBG
|
|
6116
|
+
lognow("DEBUG : Server cell receives message from incoming...");
|
|
6117
|
+
|
|
6118
|
+
self.executeListeners("incoming", channelName, message, clientSocket);
|
|
6119
|
+
|
|
6120
|
+
// DBG
|
|
6121
|
+
lognow("DEBUG : Server cell receives message from both (in)...");
|
|
6122
|
+
|
|
5681
6123
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
5682
6124
|
});
|
|
5683
6125
|
});
|
|
5684
6126
|
|
|
5685
6127
|
|
|
5686
|
-
// HANDLE HELLO SEQUENCE
|
|
5687
|
-
self.
|
|
6128
|
+
// HANDLE SERVER CELL HELLO SEQUENCE
|
|
6129
|
+
self.handleServerCellHelloSequence();
|
|
6130
|
+
|
|
6131
|
+
|
|
6132
|
+
// ******************************************
|
|
6133
|
+
// CLIENT
|
|
6134
|
+
|
|
6135
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
6136
|
+
self.handleClientHelloSequence();
|
|
5688
6137
|
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
6138
|
+
|
|
6139
|
+
},
|
|
6140
|
+
// On client connection lost :
|
|
6141
|
+
(clientId)=>{
|
|
6142
|
+
lognow(`INFO : Connection to client id «${clientId}» was lost.`);
|
|
6143
|
+
// TRACE
|
|
6144
|
+
lognow(`INFO : Removing client information for client id «${clientId}».`);
|
|
6145
|
+
self.lobuleZone.remove(clientId);
|
|
6146
|
+
},
|
|
6147
|
+
this.port, this.sslConfig.certPath, this.sslConfig.keyPath);
|
|
6148
|
+
|
|
6149
|
+
serverWrapper.serverManager.start();
|
|
6150
|
+
return serverWrapper;
|
|
5692
6151
|
}
|
|
5693
6152
|
|
|
5694
6153
|
/*private*/probeOutcomingNetwork(){
|
|
6154
|
+
|
|
6155
|
+
const self=this;
|
|
6156
|
+
|
|
5695
6157
|
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
5696
6158
|
let numberOfConnectedOutcomingServers=0;
|
|
5697
6159
|
foreach(this.outcomingCells, outcomingCell=>{
|
|
@@ -5713,7 +6175,6 @@ class AORTACServerCell{
|
|
|
5713
6175
|
return;
|
|
5714
6176
|
}
|
|
5715
6177
|
|
|
5716
|
-
const self=this;
|
|
5717
6178
|
|
|
5718
6179
|
// We try to connect to all outcoming cells :
|
|
5719
6180
|
foreach(this.outcomingCells, (outcomingCell, outcomingCellOrigin)=>{
|
|
@@ -5725,20 +6186,29 @@ class AORTACServerCell{
|
|
|
5725
6186
|
|
|
5726
6187
|
const clientInstance=initClient(true, false, (socketToServerClientInstance)=>{
|
|
5727
6188
|
|
|
6189
|
+
|
|
6190
|
+
const channelsNames=[];
|
|
6191
|
+
foreach(Object.keys(self.onReceiveMessageListeners["outcoming"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
6192
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
6193
|
+
|
|
6194
|
+
|
|
5728
6195
|
// On-receive message listeners handling ;
|
|
5729
|
-
foreach(
|
|
6196
|
+
foreach(channelsNames, channelName=>{
|
|
5730
6197
|
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
6198
|
+
|
|
6199
|
+
// DBG
|
|
6200
|
+
lognow("DEBUG : Server cell receives message from outcoming...");
|
|
6201
|
+
|
|
5731
6202
|
self.executeListeners("outcoming",channelName, message, clientSocket);
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
6203
|
+
|
|
6204
|
+
// DBG
|
|
6205
|
+
lognow("DEBUG : Server cell receives message from both (out)...");
|
|
6206
|
+
|
|
5736
6207
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
5737
6208
|
});
|
|
5738
6209
|
});
|
|
5739
|
-
|
|
5740
6210
|
|
|
5741
|
-
// INITIATE HELLO SEQUENCE
|
|
6211
|
+
// INITIATE SERVER CELL HELLO SEQUENCE
|
|
5742
6212
|
self.initiateHelloSequence(socketToServerClientInstance);
|
|
5743
6213
|
|
|
5744
6214
|
// We are connected:
|
|
@@ -5762,47 +6232,68 @@ class AORTACServerCell{
|
|
|
5762
6232
|
|
|
5763
6233
|
}
|
|
5764
6234
|
|
|
5765
|
-
|
|
5766
6235
|
/*private*/handleCommonListeners(){
|
|
5767
|
-
|
|
5768
6236
|
// HANDLE CELL IS READY SEQUENCE
|
|
5769
6237
|
this.handleCellIsReadySequence();
|
|
5770
|
-
|
|
5771
6238
|
// HANDLE PARTITION SEQUENCE
|
|
5772
6239
|
this.handlePartitionSequence();
|
|
5773
6240
|
|
|
5774
|
-
|
|
6241
|
+
|
|
6242
|
+
// ******************************************
|
|
6243
|
+
// CLIENT
|
|
6244
|
+
// HANDLE CLIENT MODEL INITIAL POPULATION SEQUENCE
|
|
6245
|
+
this.handleGetServersForZoneRequest();
|
|
6246
|
+
|
|
6247
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
6248
|
+
this.handleReconnectedClientHelloSequence();
|
|
5775
6249
|
|
|
6250
|
+
}
|
|
6251
|
+
|
|
5776
6252
|
|
|
5777
6253
|
/*private*/executeListeners(outletName, channelName, message, clientSocket){
|
|
5778
6254
|
const onReceiveMessageListeners=this.onReceiveMessageListeners[outletName][channelName];
|
|
5779
6255
|
if(!onReceiveMessageListeners) return;
|
|
5780
6256
|
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
6257
|
+
const self=this;
|
|
6258
|
+
foreach(onReceiveMessageListeners, (listener, listenerMessageType)=>{
|
|
6259
|
+
listener.execute(self, message, self.serverWrapper, clientSocket);
|
|
6260
|
+
},(listener)=>(message.type===listener.listenerMessageType));
|
|
6261
|
+
|
|
6262
|
+
}
|
|
6263
|
+
|
|
6264
|
+
|
|
6265
|
+
/*private*/replyToBlobRequest(channelName, originalMessage, message){
|
|
6266
|
+
if(!message.originatingCellOrigin)
|
|
6267
|
+
message.originatingCellOrigin=originalMessage.originatingCellOrigin;
|
|
6268
|
+
if(!message.originatingPartitionId)
|
|
6269
|
+
message.originatingPartitionId=originalMessage.originatingPartitionId;
|
|
6270
|
+
if(!message.clientId)
|
|
6271
|
+
message.clientId=originalMessage.clientId;
|
|
6272
|
+
return this.sendMessageToBlob(channelName, message,
|
|
6273
|
+
{
|
|
6274
|
+
isOriginatingCell:false,
|
|
6275
|
+
destinationCellsOrigins:[originalMessage.originatingCellOrigin],
|
|
6276
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
6277
|
+
isRequest:false
|
|
6278
|
+
},
|
|
6279
|
+
originalMessage.type+".response");
|
|
5795
6280
|
}
|
|
5796
6281
|
|
|
5797
6282
|
|
|
5798
6283
|
/*private*/sendMessageToBlob(channelName, message,
|
|
5799
|
-
broadcastConfig={
|
|
6284
|
+
broadcastConfig={
|
|
6285
|
+
isOriginatingCell:false,
|
|
6286
|
+
destinationCellsOrigins:null,
|
|
6287
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
6288
|
+
isRequest:false,
|
|
6289
|
+
contactAllQuorumBlob:false
|
|
6290
|
+
},
|
|
6291
|
+
overridingMessageType=null){
|
|
5800
6292
|
|
|
5801
|
-
const self=this;
|
|
5802
|
-
|
|
5803
6293
|
if(broadcastConfig){
|
|
5804
6294
|
if(broadcastConfig.isOriginatingCell){
|
|
5805
6295
|
message.originatingCellOrigin=this.selfOrigin;
|
|
6296
|
+
message.originatingPartitionId=this.partitionId;
|
|
5806
6297
|
}
|
|
5807
6298
|
if(broadcastConfig.isRequest){
|
|
5808
6299
|
message.isRequest=true;
|
|
@@ -5815,10 +6306,36 @@ class AORTACServerCell{
|
|
|
5815
6306
|
if(!message.visitedCells)
|
|
5816
6307
|
message.visitedCells=[];
|
|
5817
6308
|
else if(contains(message.visitedCells,this.selfOrigin))
|
|
5818
|
-
return;
|
|
6309
|
+
return null;
|
|
5819
6310
|
message.visitedCells.push(this.selfOrigin);
|
|
5820
6311
|
|
|
5821
|
-
|
|
6312
|
+
|
|
6313
|
+
// AUTOMATIC LISTENER SETUP :
|
|
6314
|
+
const messageTypeForResponse=nonull(overridingMessageType, message.type+".response");
|
|
6315
|
+
let blobResponseListener=null;
|
|
6316
|
+
// We only add a result listener for the one cell which sent the request message (we skip the listener adding for the others in this case) :
|
|
6317
|
+
if(message.isRequest && message.originatingCellOrigin==this.selfOrigin
|
|
6318
|
+
//&& (typeof(message.result)=="undefined" || message.result==null)
|
|
6319
|
+
){
|
|
6320
|
+
// To emulate a promise-like behavior :
|
|
6321
|
+
blobResponseListener=this.getBlobResponseListenerForRequest(message, channelName, broadcastConfig, messageTypeForResponse);
|
|
6322
|
+
|
|
6323
|
+
const outletName=nonull(blobResponseListener.outletName,"both");
|
|
6324
|
+
if(!this.onReceiveMessageListeners[outletName][channelName][blobResponseListener.listenerMessageType]){
|
|
6325
|
+
this.onReceiveMessageListeners[outletName][channelName][blobResponseListener.listenerMessageType]=blobResponseListener;
|
|
6326
|
+
// TRACE
|
|
6327
|
+
lognow("INFO : Added automatic listener for message type «"+blobResponseListener.listenerMessageType+"».");
|
|
6328
|
+
}else{
|
|
6329
|
+
// TRACE
|
|
6330
|
+
lognow("INFO : Listener for message type «"+blobResponseListener.listenerMessageType+"» already existed. Done nothing.");
|
|
6331
|
+
}
|
|
6332
|
+
}else{
|
|
6333
|
+
// TRACE
|
|
6334
|
+
lognow(`INFO : No listener to setup, because broadcast is not a request, or cell is not the originating cell !`);
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6337
|
+
// SENDING
|
|
6338
|
+
if(broadcastConfig && !broadcastConfig.excludeIncomingServersCellsAndClientsInTransmission){
|
|
5822
6339
|
foreach(this.incomingCells,(incomingCell)=>{
|
|
5823
6340
|
// As a server, we send (forward) the message to the currently iterated upon client that is connected to us :
|
|
5824
6341
|
incomingCell.server.send(channelName, message, null, incomingCell.clientSocket);
|
|
@@ -5826,7 +6343,6 @@ class AORTACServerCell{
|
|
|
5826
6343
|
(incomingCell.connected && !contains(message.visitedCells, incomingCellOrigin))
|
|
5827
6344
|
);
|
|
5828
6345
|
}
|
|
5829
|
-
|
|
5830
6346
|
foreach(this.outcomingCells,(outcomingCell)=>{
|
|
5831
6347
|
// As a client, we send (forward) the message to the currently iterated upon server we are connected to :
|
|
5832
6348
|
outcomingCell.socketToServerClientInstance.send(channelName, message);
|
|
@@ -5834,49 +6350,84 @@ class AORTACServerCell{
|
|
|
5834
6350
|
(outcomingCell.connected && !contains(message.visitedCells, outcomingCellOrigin))
|
|
5835
6351
|
);
|
|
5836
6352
|
|
|
6353
|
+
return blobResponseListener;
|
|
6354
|
+
}
|
|
6355
|
+
|
|
6356
|
+
|
|
6357
|
+
/*private*/getBlobResponseListenerForRequest(message, channelName, broadcastConfig, messageTypeForResponse){
|
|
6358
|
+
|
|
6359
|
+
const outletName="both"; // «incoming» & «outcoming»
|
|
6360
|
+
// We want to monitor when the server cell receives the RESPONSE of the message it sent as request !!!
|
|
6361
|
+
|
|
6362
|
+
// First we check if the listener for request already exists :
|
|
6363
|
+
let blobResponseListener=this.onReceiveMessageListeners[outletName][channelName][messageTypeForResponse];
|
|
6364
|
+
if(blobResponseListener){
|
|
6365
|
+
// TRACE
|
|
6366
|
+
lognow(`INFO : Blob response listener for cell ${this.selfOrigin} on channel ${channelName} for message type «${messageTypeForResponse}» already exists.`);
|
|
6367
|
+
return blobResponseListener;
|
|
6368
|
+
}
|
|
5837
6369
|
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
// When we have received the message :
|
|
5859
|
-
this.thenCallback(message);
|
|
6370
|
+
|
|
6371
|
+
blobResponseListener={
|
|
6372
|
+
broadcastConfig:broadcastConfig,
|
|
6373
|
+
thenCallback:null,
|
|
6374
|
+
allBlobHasBeenContactedCallback:null,
|
|
6375
|
+
hasAllBlobBeenContacted:false,
|
|
6376
|
+
// We use the partition ids to know which server cells have been contacted :
|
|
6377
|
+
partitionIdsThatCouldBeContacted:[],
|
|
6378
|
+
// This message will be the RESPONSE to the REQUEST (ti will be of type «<messageTypeForResponse>», meaning the request message type + «.response» !!)
|
|
6379
|
+
outletName:outletName,
|
|
6380
|
+
listenerMessageType:messageTypeForResponse,
|
|
6381
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6382
|
+
|
|
6383
|
+
// This is what the requesting cell does every time any cell that is not the requesting cell sends back a response to its request to blob :
|
|
6384
|
+
|
|
6385
|
+
// When we have received the message :
|
|
6386
|
+
if(blobResponseListener.thenCallback) blobResponseListener.thenCallback(selfParam, messageParam);
|
|
6387
|
+
|
|
6388
|
+
// We try to determine if the whole blob has been contacted or not :
|
|
6389
|
+
if(blobResponseListener.broadcastConfig.contactAllQuorumBlob && !blobResponseListener.hasAllBlobBeenContacted){
|
|
5860
6390
|
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
6391
|
+
if(!contains(blobResponseListener.partitionIdsThatCouldBeContacted, messageParam.originatingPartitionId))
|
|
6392
|
+
blobResponseListener.partitionIdsThatCouldBeContacted.push(messageParam.originatingPartitionId);
|
|
6393
|
+
// We need to exclude the current serevr cell itself :
|
|
6394
|
+
if(selfParam.numberOfPartitions-1<=blobResponseListener.partitionIdsThatCouldBeContacted.length){
|
|
6395
|
+
if(blobResponseListener.allBlobHasBeenContactedCallback)
|
|
6396
|
+
blobResponseListener.allBlobHasBeenContactedCallback(selfParam, messageParam);
|
|
6397
|
+
blobResponseListener.hasAllBlobBeenContacted=true;
|
|
6398
|
+
}
|
|
5866
6399
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
6400
|
+
|
|
6401
|
+
// We need to remove the result listener once it has been completed though.
|
|
6402
|
+
if(!blobResponseListener.broadcastConfig.contactAllQuorumBlob || blobResponseListener.hasAllBlobBeenContacted){
|
|
6403
|
+
//UNUSEFUL : delete blobResponseListeners[listenerTypeForRequest];
|
|
6404
|
+
delete selfParam.onReceiveMessageListeners[outletName][channelName][messageTypeForResponse];
|
|
6405
|
+
}
|
|
6406
|
+
|
|
6407
|
+
},
|
|
6408
|
+
thenOnAnyResponseFromBlobRequestingCellOnly:(thenCallback)=>{
|
|
6409
|
+
blobResponseListener.thenCallback=thenCallback;
|
|
6410
|
+
return blobResponseListener;
|
|
6411
|
+
},
|
|
6412
|
+
doOnceAllBlobHasBeenContacted:(allBlobHasBeenContactedCallback)=>{
|
|
6413
|
+
blobResponseListener.allBlobHasBeenContactedCallback=allBlobHasBeenContactedCallback;
|
|
6414
|
+
return blobResponseListener;
|
|
6415
|
+
},
|
|
6416
|
+
};
|
|
6417
|
+
|
|
5874
6418
|
|
|
6419
|
+
|
|
6420
|
+
return blobResponseListener;
|
|
5875
6421
|
}
|
|
6422
|
+
|
|
6423
|
+
|
|
5876
6424
|
|
|
6425
|
+
// ===================================================================================================
|
|
6426
|
+
// SERVER CELLS
|
|
6427
|
+
// ===================================================================================================
|
|
5877
6428
|
|
|
5878
6429
|
// ==========================================================
|
|
5879
|
-
// HELLO SEQUENCE
|
|
6430
|
+
// SERVER CELL HELLO SEQUENCE
|
|
5880
6431
|
|
|
5881
6432
|
/*private*/initiateHelloSequence(socketToServerClientInstance){
|
|
5882
6433
|
|
|
@@ -5889,28 +6440,30 @@ class AORTACServerCell{
|
|
|
5889
6440
|
}
|
|
5890
6441
|
|
|
5891
6442
|
|
|
5892
|
-
/*private*/
|
|
6443
|
+
/*private*/handleServerCellHelloSequence(){
|
|
5893
6444
|
|
|
5894
6445
|
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
6446
|
+
this.onReceiveMessageListeners["both"]["protocol"]["helloRequest"]={
|
|
6447
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
5898
6448
|
|
|
5899
|
-
const cellOriginToCheck=
|
|
6449
|
+
const cellOriginToCheck=messageParam.originatingCellOrigin;
|
|
5900
6450
|
|
|
5901
6451
|
// TRACE
|
|
5902
|
-
lognow(`INFO : Incoming node ${cellOriginToCheck} has said hello. Updating its local information...`);
|
|
6452
|
+
lognow(`INFO : (handleServerCellHelloSequence()) Incoming node ${cellOriginToCheck} has said hello. Updating its local information...`);
|
|
5903
6453
|
|
|
5904
6454
|
|
|
5905
6455
|
selfParam.incomingCells[cellOriginToCheck]={
|
|
5906
6456
|
connected:true,
|
|
5907
|
-
server:server,
|
|
6457
|
+
server:serverWrapper.server,
|
|
5908
6458
|
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
5909
6459
|
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
5910
6460
|
clientSocket:clientSocket,
|
|
5911
6461
|
};
|
|
5912
6462
|
|
|
5913
|
-
|
|
6463
|
+
|
|
6464
|
+
// NO SERVER CELL HELLO CONFIRMATION SENT BACK TO THE OTHER SERVER CELL (to save time)
|
|
6465
|
+
|
|
6466
|
+
},
|
|
5914
6467
|
listenerMessageType:"helloRequest"
|
|
5915
6468
|
};
|
|
5916
6469
|
|
|
@@ -5935,18 +6488,14 @@ class AORTACServerCell{
|
|
|
5935
6488
|
}
|
|
5936
6489
|
|
|
5937
6490
|
/*private*/handleCellIsReadySequence(){
|
|
5938
|
-
getOrCreateEmptyAttribute(
|
|
5939
|
-
this.onReceiveMessageListeners["both"]["protocol"],"cellIsReady")["forRequestsIssuedByOthers"]={
|
|
5940
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
5941
6491
|
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
6492
|
+
this.onReceiveMessageListeners["both"]["protocol"]["cellIsReady"]={
|
|
6493
|
+
|
|
6494
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6495
|
+
|
|
6496
|
+
selfParam.cellsOverview[messageParam.originatingCellOrigin]={ready:true,startTime:messageParam.startTime};
|
|
5945
6497
|
|
|
5946
|
-
|
|
5947
|
-
// lognow("INFO : Updated cells overview for this cell ("+selfParam.selfOrigin+") :",selfParam.cellsOverview);
|
|
5948
|
-
// lognow("DEBUG : selfParam.outcomingCells :",Object.keys(selfParam.outcomingCells));
|
|
5949
|
-
// lognow("DEBUG : selfParam.incomingCells :",Object.keys(selfParam.incomingCells));
|
|
6498
|
+
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
5950
6499
|
|
|
5951
6500
|
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
5952
6501
|
selfParam.initiateQuorumIsReachedSequenceIfNecessary();
|
|
@@ -5972,11 +6521,11 @@ class AORTACServerCell{
|
|
|
5972
6521
|
const quorumCells=copy(this.cellsOverview);
|
|
5973
6522
|
|
|
5974
6523
|
// TRACE
|
|
5975
|
-
lognow("INFO : Quorum is reached and this is the latest started cell. quorumCells
|
|
5976
|
-
|
|
5977
|
-
this.
|
|
5978
|
-
|
|
5979
|
-
|
|
6524
|
+
lognow("INFO : Quorum is reached and this is the latest started cell (QLC). quorumCells:",quorumCells);
|
|
6525
|
+
|
|
6526
|
+
this.isServerCellQuorumLastCell=true;
|
|
6527
|
+
|
|
6528
|
+
this.launchModelPartitionBlobSequence(quorumCells);
|
|
5980
6529
|
|
|
5981
6530
|
}
|
|
5982
6531
|
|
|
@@ -5987,249 +6536,694 @@ class AORTACServerCell{
|
|
|
5987
6536
|
// ==========================================================
|
|
5988
6537
|
// MODEL MANAGEMENT
|
|
5989
6538
|
|
|
5990
|
-
/*private*/
|
|
5991
|
-
|
|
5992
|
-
const self=this;
|
|
5993
|
-
|
|
5994
|
-
|
|
6539
|
+
/*private*/async launchModelPartitionBlobSequence(quorumCells){
|
|
6540
|
+
|
|
6541
|
+
const self=this;
|
|
6542
|
+
|
|
6543
|
+
this.numberOfPartitions=getArraySize(quorumCells);
|
|
6544
|
+
|
|
6545
|
+
const controller=this.controller;
|
|
6546
|
+
// const model=this.model;
|
|
6547
|
+
|
|
6548
|
+
const partitionsInstantiationZones=controller.getPartitionsZones(this.numberOfPartitions);
|
|
6549
|
+
|
|
6550
|
+
const firstPartitionZone=getAt(partitionsInstantiationZones,0);
|
|
6551
|
+
|
|
6552
|
+
// We populate and initialize the server cell model, using the first partition zone :
|
|
6553
|
+
const model=await this.doOnModelZonePartitionReception(firstPartitionZone, this.selfOrigin);
|
|
6554
|
+
|
|
6555
|
+
// DBG
|
|
6556
|
+
lognow("DEBUG : MODEL WAS JUST CREATED.");
|
|
6557
|
+
|
|
6558
|
+
// We send the models partition zones objects to the required cells :
|
|
6559
|
+
const qlcRootContainer=self.partiallyPopulatedRootContainer();
|
|
6560
|
+
|
|
6561
|
+
let i=1;
|
|
6562
|
+
foreach(quorumCells, (quorumCell, quorumCellOrigin)=>{
|
|
6563
|
+
|
|
6564
|
+
const modelPartitionZone=getAt(partitionsInstantiationZones,i);
|
|
6565
|
+
|
|
6566
|
+
const message={qlcOrigin: self.selfOrigin, qlcRootContainer:qlcRootContainer, type:"modelPartitionZone", partitionZone:modelPartitionZone};
|
|
6567
|
+
|
|
6568
|
+
// TRACE
|
|
6569
|
+
lognow(`INFO : Server cell ${self.selfOrigin} is sending a partition zone to server cell ${quorumCellOrigin}...`, message);
|
|
6570
|
+
|
|
6571
|
+
self.sendMessageToBlob("protocol", message,
|
|
6572
|
+
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin],
|
|
6573
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
6574
|
+
isRequest:false});
|
|
6575
|
+
|
|
6576
|
+
i++;
|
|
6577
|
+
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
6578
|
+
|
|
6579
|
+
|
|
6580
|
+
// Each quorum member cell is responsible for a model partition
|
|
6581
|
+
// Then the sattelite cells will be handling the duplication
|
|
6582
|
+
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
|
|
6586
|
+
// HANDLE PARTITION SEQUENCE
|
|
6587
|
+
|
|
6588
|
+
/*private*/handlePartitionSequence(){
|
|
6589
|
+
|
|
6590
|
+
// CAUTION : For *non-QLC* server cells only :
|
|
6591
|
+
|
|
6592
|
+
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
6593
|
+
this.onReceiveMessageListeners["both"]["protocol"]["modelPartitionZone"]={
|
|
6594
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6595
|
+
|
|
6596
|
+
// If this cell is in the destinations of this message, we pass it along and then we do what it says :
|
|
6597
|
+
selfParam.sendMessageToBlob("protocol", messageParam);
|
|
6598
|
+
|
|
6599
|
+
if(contains(messageParam.destinationCellsOrigins, selfParam.selfOrigin)){
|
|
6600
|
+
|
|
6601
|
+
// We populate and initialize the server cell model :
|
|
6602
|
+
selfParam.doOnModelZonePartitionReception(messageParam.partitionZone, messageParam.qlcOrigin, messageParam.qlcRootContainer).then(model=>{
|
|
6603
|
+
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6606
|
+
|
|
6607
|
+
},
|
|
6608
|
+
listenerMessageType:"modelPartitionZone"
|
|
6609
|
+
};
|
|
6610
|
+
|
|
6611
|
+
}
|
|
6612
|
+
|
|
6613
|
+
|
|
6614
|
+
/*private*/doOnModelZonePartitionReception(partitionZone, qlcOrigin, qlcRootContainer=null){
|
|
6615
|
+
|
|
6616
|
+
return new Promise((resolve,reject)=>{
|
|
6617
|
+
|
|
6618
|
+
// TRACE
|
|
6619
|
+
lognow(`INFO : Applying partition zone for cell ${this.selfOrigin}. Updating local server model...`);
|
|
6620
|
+
lognow(`DEBUG : partitionZone:`, partitionZone);
|
|
6621
|
+
|
|
6622
|
+
this.partitionId=partitionZone.id;
|
|
6623
|
+
|
|
6624
|
+
// TRACE
|
|
6625
|
+
lognow("INFO : Populating model for this partition zone...");
|
|
6626
|
+
|
|
6627
|
+
if(this.isServerCellQuorumLastCell){ // Case QLC :
|
|
6628
|
+
|
|
6629
|
+
// DBG
|
|
6630
|
+
lognow("DEBUG : (this cell is the last quorum server cell (QLC)) ");
|
|
6631
|
+
|
|
6632
|
+
// We initialize the portion of the model :
|
|
6633
|
+
// (controller will handle if a persisted model already exists)
|
|
6634
|
+
// (in this particular case, qlcOrigin and this.selfOrigin is exactly the same !)
|
|
6635
|
+
this.controller.populateModelInZone(this.model, partitionZone, getHashedString(qlcOrigin), getHashedString(this.selfOrigin) ).then((model)=>{
|
|
6636
|
+
|
|
6637
|
+
// DBG
|
|
6638
|
+
lognow("DEBUG : model is populated in QLC server cell.");
|
|
6639
|
+
|
|
6640
|
+
resolve(model);
|
|
6641
|
+
});
|
|
6642
|
+
|
|
6643
|
+
}else{ // Case NON-QLC :
|
|
6644
|
+
|
|
6645
|
+
|
|
6646
|
+
// BASICALLY IT'S THE EXACT SAME TREATMENT AS FOR A BLOB CLIENT !
|
|
6647
|
+
|
|
6648
|
+
// DBG
|
|
6649
|
+
lognow("DEBUG : (this cell is *NOT* last quorum server cell (NON-QLC)) ");
|
|
6650
|
+
|
|
6651
|
+
|
|
6652
|
+
// We initialize the portion of the model :
|
|
6653
|
+
// (controller will handle if a persisted model already exists)
|
|
6654
|
+
this.controller.populateModelInZone(this.model, partitionZone, getHashedString(qlcOrigin), getHashedString(this.selfOrigin), qlcRootContainer ).then((model)=>{
|
|
6655
|
+
|
|
6656
|
+
// DBG
|
|
6657
|
+
lognow("DEBUG : model is populated in NON-QLC server cell.");
|
|
6658
|
+
|
|
6659
|
+
resolve(model);
|
|
6660
|
+
});
|
|
6661
|
+
|
|
6662
|
+
}
|
|
6663
|
+
|
|
6664
|
+
|
|
6665
|
+
});
|
|
6666
|
+
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
|
|
6670
|
+
// ===================================================================================================
|
|
6671
|
+
// CLIENT
|
|
6672
|
+
// ===================================================================================================
|
|
6673
|
+
|
|
6674
|
+
// ==========================================================
|
|
6675
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
6676
|
+
|
|
6677
|
+
handleClientHelloSequence(){
|
|
6678
|
+
|
|
6679
|
+
// We wait to receive the hello request of the incoming client connection :
|
|
6680
|
+
this.onReceiveMessageListeners["incoming"]["protocol"]["clientHello"]={
|
|
6681
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6682
|
+
|
|
6683
|
+
const clientId=messageParam.clientId;
|
|
6684
|
+
const manifestationZone=messageParam.manifestationZone;
|
|
6685
|
+
|
|
6686
|
+
// TRACE
|
|
6687
|
+
lognow(`INFO : (handleClientHelloSequence()) Incoming client ${clientId} has said hello. Starting its reconnection sequence...`);
|
|
6688
|
+
|
|
6689
|
+
// We send the servers on which the client must reconnect to :
|
|
6690
|
+
|
|
6691
|
+
// Init lobule :
|
|
6692
|
+
selfParam.lobuleZone.clear(clientId);
|
|
6693
|
+
selfParam.lobuleZone.setClientConnectionInfo(clientId, serverWrapper, clientSocket);
|
|
6694
|
+
|
|
6695
|
+
// Caution : client must ignore this server cell if it has no objects for it !
|
|
6696
|
+
let allObjectsCorrespondingToZoneInServerCell=[];
|
|
6697
|
+
try{
|
|
6698
|
+
selfParam.model.checkAllMethodsCustomModelClassPrerequisites();
|
|
6699
|
+
allObjectsCorrespondingToZoneInServerCell=selfParam.model.getAllPartitionnableObjectsInZone(manifestationZone);
|
|
6700
|
+
}catch(e){
|
|
6701
|
+
//TRACE
|
|
6702
|
+
lognow(e);
|
|
6703
|
+
}
|
|
6704
|
+
|
|
6705
|
+
if(!empty(allObjectsCorrespondingToZoneInServerCell)){
|
|
6706
|
+
selfParam.lobuleZone.append(clientId, [selfParam.selfOrigin], "serversBag");
|
|
6707
|
+
selfParam.lobuleZone.append(clientId, allObjectsCorrespondingToZoneInServerCell);
|
|
6708
|
+
}else{
|
|
6709
|
+
// TRACE
|
|
6710
|
+
lognow("INFO : Server cell has no relevant objects to send to client");
|
|
6711
|
+
}
|
|
6712
|
+
|
|
6713
|
+
// The QLC in all cases, sends its structural objects to client :
|
|
6714
|
+
if(selfParam.isServerCellQuorumLastCell){
|
|
6715
|
+
// Case this server cell is the QLC :
|
|
6716
|
+
const rootContainer=selfParam.partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell);
|
|
6717
|
+
// Then we send the structure and, if any, the concerned objects in client manifestation zone of this server cell :
|
|
6718
|
+
selfParam.lobuleZone.append(clientId, [rootContainer]);
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
// At this point , in the QLC server cell's lobule zone for this client we have :
|
|
6722
|
+
// (- maybe its concerned zone objects.)
|
|
6723
|
+
// - its root container
|
|
6724
|
+
|
|
6725
|
+
|
|
6726
|
+
// We ask the blob
|
|
6727
|
+
selfParam.sendMessageToBlob("protocol", {type:"getServersForZone", clientId:clientId, manifestationZone:manifestationZone},
|
|
6728
|
+
{isOriginatingCell:true,
|
|
6729
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
6730
|
+
isRequest:true,
|
|
6731
|
+
contactAllQuorumBlob:true})
|
|
6732
|
+
.thenOnAnyResponseFromBlobRequestingCellOnly((selfParam2, messageParamLocal)=>{
|
|
6733
|
+
// Triggered every time this server cell receives a response to its request from blob :
|
|
6734
|
+
|
|
6735
|
+
const clientIdLocal=messageParamLocal.clientId;
|
|
6736
|
+
const answeringServerOrigin=messageParamLocal.answeringServerOrigin;
|
|
6737
|
+
const objectsCount=messageParamLocal.objectsCount;
|
|
6738
|
+
|
|
6739
|
+
// DBG
|
|
6740
|
+
lognow("DEBUG : This server cell has received a response from another server cell : answeringServerOrigin:",answeringServerOrigin);
|
|
6741
|
+
|
|
6742
|
+
// When another server cells answers to this server cell :
|
|
6743
|
+
|
|
6744
|
+
if(!objectsCount || objectsCount<=0){
|
|
6745
|
+
// TRACE
|
|
6746
|
+
lognow("INFO : No objects corresponding to this zone to send to collector server cell. This server cell must be ignored.");
|
|
6747
|
+
}else{
|
|
6748
|
+
// We accumulate the server origin in the lobule :
|
|
6749
|
+
selfParam2.lobuleZone.append(clientIdLocal, [answeringServerOrigin], "serversBag");
|
|
6750
|
+
}
|
|
6751
|
+
|
|
6752
|
+
|
|
6753
|
+
}).doOnceAllBlobHasBeenContacted((selfParam2, messageParamLocal)=>{
|
|
6754
|
+
|
|
6755
|
+
// Triggered once the requesting server cell has received the response from the last possible other server cell, according to the partition ids !
|
|
6756
|
+
|
|
6757
|
+
const clientIdLocal=messageParamLocal.clientId;
|
|
6758
|
+
|
|
6759
|
+
// At this point we should have all the needed servers in the lobule :
|
|
6760
|
+
const servers=selfParam2.lobuleZone.getServers(clientIdLocal);
|
|
6761
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
6762
|
+
const objects=selfParam2.lobuleZone.getObjects(clientIdLocal);
|
|
6763
|
+
|
|
6764
|
+
// ???
|
|
6765
|
+
// if(!empty(objects)){
|
|
6766
|
+
// selfParam2.removeLinks(objects);
|
|
6767
|
+
// }
|
|
6768
|
+
|
|
6769
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
6770
|
+
const messageWrapped=JSON.decycle({type:"clientHello.response", servers:servers, objects:objects});
|
|
5995
6771
|
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
controller.initializeModelForAORTACNode(model);
|
|
6003
|
-
|
|
6004
|
-
// This must return the asked number of partitions, indexed by partition id :
|
|
6005
|
-
// CAUTION : The partition function MUST return ALL the objects in a partition, WHATEVER THEIR NESTING LEVEL !!!
|
|
6006
|
-
const modelPartitions=model.getPartitions(numberOfPartitions);
|
|
6772
|
+
// DBG
|
|
6773
|
+
lognow("DEBUG : All blob has been contacted : messageWrapped:",messageWrapped);
|
|
6774
|
+
|
|
6775
|
+
// We send back all the collected servers to the requesting client :
|
|
6776
|
+
const clientConnectionInfo=selfParam2.lobuleZone.getClientConnectionInfo(clientIdLocal);
|
|
6777
|
+
clientConnectionInfo.serverWrapper.server.send("protocol", messageWrapped, null, clientConnectionInfo.clientSocket);
|
|
6007
6778
|
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
// We send the models objects to the required cells :
|
|
6013
|
-
let i=1;
|
|
6014
|
-
foreach(quorumCells, (quorumCell,quorumCellOrigin)=>{
|
|
6779
|
+
selfParam2.lobuleZone.clear(clientIdLocal);
|
|
6780
|
+
|
|
6781
|
+
});
|
|
6782
|
+
|
|
6015
6783
|
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
const message=JSON.decycle({type:"modelPartition",partition:modelPartition});
|
|
6020
|
-
|
|
6021
|
-
// TRACE
|
|
6022
|
-
lognow(`INFO : Cell ${self.selfOrigin} is sending a partition to cell ${quorumCellOrigin}...`,message);
|
|
6023
|
-
|
|
6024
|
-
self.sendMessageToBlob("protocol", message,
|
|
6025
|
-
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin], includeIncomingConnectionInTransmission:false, isRequest:false});
|
|
6784
|
+
},
|
|
6785
|
+
listenerMessageType:"clientHello"
|
|
6786
|
+
};
|
|
6026
6787
|
|
|
6027
|
-
i++;
|
|
6028
|
-
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
// Each quorum member cell is responsible for a model partition
|
|
6032
|
-
// Then the sattelite cells will be handling the duplication
|
|
6033
|
-
|
|
6034
6788
|
}
|
|
6035
|
-
|
|
6036
6789
|
|
|
6037
|
-
// HANDLE PARTITION SEQUENCE
|
|
6038
6790
|
|
|
6039
|
-
|
|
6791
|
+
// This is what other cells do when they receive a request for «getServersForZone» from a cell in the blob
|
|
6792
|
+
/*private*/handleGetServersForZoneRequest(){
|
|
6040
6793
|
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6794
|
+
this.onReceiveMessageListeners["both"]["protocol"]["getServersForZone"]={
|
|
6795
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6796
|
+
|
|
6797
|
+
// DBG
|
|
6798
|
+
lognow("DEBUG : handleGetServersForZoneRequest() : messageParam", messageParam);
|
|
6799
|
+
|
|
6800
|
+
const clientId=messageParam.clientId;
|
|
6801
|
+
const manifestationZone=messageParam.manifestationZone;
|
|
6802
|
+
|
|
6803
|
+
// When we receive a request from a server cell :
|
|
6804
|
+
let allObjectsCorrespondingToZoneInServerCell=[];
|
|
6805
|
+
try{
|
|
6806
|
+
selfParam.model.checkAllMethodsCustomModelClassPrerequisites();
|
|
6807
|
+
allObjectsCorrespondingToZoneInServerCell=selfParam.model.getAllPartitionnableObjectsInZone(manifestationZone);
|
|
6808
|
+
}catch(e){
|
|
6809
|
+
// TRACE
|
|
6810
|
+
lognow(e);
|
|
6811
|
+
}
|
|
6812
|
+
|
|
6813
|
+
// We store the objects for this client, because we know it will very soon ask them :
|
|
6814
|
+
// Init lobule :
|
|
6815
|
+
selfParam.lobuleZone.clear(clientId);
|
|
6816
|
+
selfParam.lobuleZone.setClientConnectionInfo(clientId, serverWrapper, clientSocket);
|
|
6817
|
+
|
|
6818
|
+
selfParam.lobuleZone.append(clientId, allObjectsCorrespondingToZoneInServerCell);
|
|
6819
|
+
|
|
6045
6820
|
|
|
6046
|
-
|
|
6047
|
-
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
6821
|
+
const message={type:"getServersForZone.response", objectsCount:allObjectsCorrespondingToZoneInServerCell.length, answeringServerOrigin:selfParam.selfOrigin};
|
|
6048
6822
|
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
const message=JSON.recycle(messageParam);
|
|
6052
|
-
|
|
6053
|
-
selfParam.doOnModelPartitionReception(message.partition);
|
|
6054
|
-
|
|
6055
|
-
}
|
|
6823
|
+
// TRACE
|
|
6824
|
+
lognow(`INFO : Server cell ${selfParam.selfOrigin} is sending its origin to server cell ${messageParam.originatingCellOrigin}...: message:`, message);
|
|
6056
6825
|
|
|
6057
|
-
|
|
6058
|
-
listenerMessageType:"
|
|
6826
|
+
selfParam.replyToBlobRequest("protocol", messageParam, message);
|
|
6827
|
+
},listenerMessageType:"getServersForZone"
|
|
6059
6828
|
};
|
|
6060
6829
|
|
|
6061
6830
|
}
|
|
6062
6831
|
|
|
6063
6832
|
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
lognow(`INFO : Incoming partition for cell ${this.selfOrigin}. Updating local model...`,partition);
|
|
6068
|
-
lognow(`>>>>`,stringifyObject(JSON.decycle(partition.objects),1));
|
|
6833
|
+
// ----
|
|
6834
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
6835
|
+
handleReconnectedClientHelloSequence(){
|
|
6069
6836
|
|
|
6837
|
+
// Here we only want to collect the servers on which to reconnect to later :
|
|
6070
6838
|
|
|
6839
|
+
// 2- We wait to receive the hello request of the incoming client connection :
|
|
6840
|
+
this.onReceiveMessageListeners["incoming"]["protocol"]["reconnectedClientHello"]={
|
|
6841
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6842
|
+
|
|
6843
|
+
const clientId=messageParam.clientId;
|
|
6844
|
+
|
|
6845
|
+
// TRACE
|
|
6846
|
+
lognow(`INFO : Incoming reconnected client ${clientId} has said hello. Updating its local information...`);
|
|
6847
|
+
|
|
6848
|
+
selfParam.clients[clientId]={
|
|
6849
|
+
connected:true,
|
|
6850
|
+
server:serverWrapper,
|
|
6851
|
+
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
6852
|
+
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
6853
|
+
clientSocket:clientSocket,
|
|
6854
|
+
};
|
|
6855
|
+
|
|
6856
|
+
const objects=selfParam.lobuleZone.getObjects(clientId);
|
|
6857
|
+
|
|
6858
|
+
// RECONNECTED CLIENT HELLO CONFIRMATION
|
|
6859
|
+
|
|
6860
|
+
|
|
6861
|
+
const messageResponse={type:"reconnectedClientHello.response", objects:objects};
|
|
6862
|
+
const messageResponseWrapped=JSON.decycle(messageResponse);
|
|
6863
|
+
|
|
6864
|
+
// DBG
|
|
6865
|
+
lognow("DEBUG : Sending «reconnectedClientHello.response»...");
|
|
6866
|
+
|
|
6867
|
+
serverWrapper.server.send("protocol", messageResponseWrapped, null, clientSocket);
|
|
6868
|
+
|
|
6869
|
+
selfParam.lobuleZone.clear(clientId);
|
|
6870
|
+
|
|
6871
|
+
},
|
|
6872
|
+
listenerMessageType:"reconnectedClientHello"
|
|
6873
|
+
};
|
|
6874
|
+
|
|
6071
6875
|
|
|
6072
6876
|
}
|
|
6073
6877
|
|
|
6074
6878
|
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
const self=this;
|
|
6078
|
-
|
|
6079
|
-
if(!currentObject){
|
|
6080
|
-
foreach(linkedObjects, obj=>{
|
|
6081
|
-
self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
6082
|
-
});
|
|
6083
|
-
}else{
|
|
6084
|
-
self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
6085
|
-
}
|
|
6086
|
-
|
|
6087
|
-
return linkedObjects;
|
|
6088
|
-
}
|
|
6879
|
+
// ==========================================================
|
|
6089
6880
|
|
|
6090
|
-
|
|
6881
|
+
|
|
6882
|
+
// ???
|
|
6883
|
+
// /*private*/removeLinks(linkedObjects, currentObject=null){
|
|
6884
|
+
// const self=this;
|
|
6885
|
+
// if(!currentObject){
|
|
6886
|
+
// foreach(linkedObjects, obj=>{
|
|
6887
|
+
// self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
6888
|
+
// });
|
|
6889
|
+
// }else{
|
|
6890
|
+
// self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
6891
|
+
// }
|
|
6892
|
+
// return linkedObjects;
|
|
6893
|
+
// }
|
|
6894
|
+
//
|
|
6895
|
+
// ???
|
|
6896
|
+
// /*private*/removeLinksOnSingleObject(linkedObjects, currentObject){
|
|
6897
|
+
// const self=this;
|
|
6898
|
+
// foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
6899
|
+
// // We only remove links to the objects not in the partition
|
|
6900
|
+
// if(isClassObject(attr)){
|
|
6901
|
+
// let aortacId=attr.aortacId;
|
|
6902
|
+
// if(!aortacId){
|
|
6903
|
+
// aortacId=getUUID();
|
|
6904
|
+
// attr.aortacId=aortacId;
|
|
6905
|
+
// }
|
|
6906
|
+
// if(!contains(linkedObjects,attr)){
|
|
6907
|
+
// currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
6908
|
+
// // CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
6909
|
+
// // WHATEVER THEIR NESTING LEVEL !!!
|
|
6910
|
+
// }
|
|
6911
|
+
// }else{
|
|
6912
|
+
// // However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
6913
|
+
// self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
6914
|
+
// }
|
|
6915
|
+
// },obj=>(isObject(obj) || isArray(obj)));
|
|
6916
|
+
// }
|
|
6091
6917
|
|
|
6092
|
-
const self=this;
|
|
6093
|
-
foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
6094
|
-
// We only remove links to the objects not in the partition
|
|
6095
|
-
if(isClassObject(attr)){
|
|
6096
|
-
let aortacId=attr.aortacId;
|
|
6097
|
-
if(!aortacId){
|
|
6098
|
-
aortacId=getUUID();
|
|
6099
|
-
attr.aortacId=aortacId;
|
|
6100
|
-
}
|
|
6101
|
-
if(!contains(linkedObjects,attr)){
|
|
6102
|
-
currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
6103
|
-
// CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
6104
|
-
// WHATEVER THEIR NESTING LEVEL !!!
|
|
6105
|
-
}
|
|
6106
|
-
}else{
|
|
6107
|
-
// However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
6108
|
-
self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
6109
|
-
}
|
|
6110
|
-
},obj=>(isObject(obj) || isArray(obj)));
|
|
6111
6918
|
|
|
6112
|
-
}
|
|
6113
6919
|
|
|
6114
6920
|
|
|
6115
|
-
//
|
|
6116
|
-
|
|
6117
|
-
/*private*/
|
|
6921
|
+
// **********************************************************************
|
|
6922
|
+
|
|
6923
|
+
/*private*/partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell=[]){
|
|
6118
6924
|
|
|
6119
|
-
|
|
6120
|
-
lognow("DEBUG : collectDependencies()...",inputObjects);
|
|
6925
|
+
const rootContainerLocal=this.model.getRootContainer();
|
|
6121
6926
|
|
|
6927
|
+
const rootContainer=this.model.populateRootContainerWithObjectsSubset(rootContainerLocal, allObjectsCorrespondingToZoneInServerCell, true);
|
|
6928
|
+
rootContainer.isRootContainer=true;
|
|
6122
6929
|
|
|
6930
|
+
return rootContainer;
|
|
6123
6931
|
}
|
|
6124
|
-
|
|
6125
|
-
/*private*/repercutChangesIfNeeded(inputObjects){
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
////
|
|
6129
|
-
lognow("DEBUG : repercutChangesIfNeeded()...",inputObjects);
|
|
6130
6932
|
|
|
6131
|
-
|
|
6132
|
-
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
6933
|
}
|
|
6136
6934
|
|
|
6137
6935
|
|
|
6138
6936
|
|
|
6139
|
-
// ******************************************************************
|
|
6140
6937
|
|
|
6141
|
-
// Public static hydration method :
|
|
6142
|
-
window.ao=(incompleteModelObjectToDecorate)=>{
|
|
6143
|
-
|
|
6144
|
-
const localServerCell=window.aortacCServerNodeInstance;
|
|
6145
|
-
if(!localServerCell){
|
|
6146
|
-
// TRACE
|
|
6147
|
-
lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
6148
|
-
return incompleteModelObjectToDecorate;
|
|
6149
|
-
}
|
|
6150
|
-
|
|
6151
|
-
const liveModelObjects=localServerCell.liveModelObjects;
|
|
6152
|
-
|
|
6153
|
-
// First we clone the object, for its attriutes values information :
|
|
6154
|
-
const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
6155
|
-
|
|
6156
|
-
// Then we replace all its methods :
|
|
6157
|
-
foreach(clonedObject, (method, methodName)=>{
|
|
6158
|
-
clonedObject[methodName]=new Proxy(method,
|
|
6159
|
-
{
|
|
6160
|
-
apply: function(methodToEnhance, thisArg, argumentsList) {
|
|
6161
6938
|
|
|
6162
|
-
const inputObjects=[thisArg];
|
|
6163
|
-
inputObjects.push(...argumentsList);
|
|
6164
|
-
localServerCell.collectDependencies(inputObjects);
|
|
6165
|
-
|
|
6166
|
-
// // --- Treatment BEFORE function execution ---
|
|
6167
|
-
// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
6168
|
-
|
|
6169
|
-
// Call the original function (target) with its intended 'this' context (thisArg)
|
|
6170
|
-
// and arguments (argumentsList) using Reflect.apply
|
|
6171
|
-
const result = Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
6172
|
-
|
|
6173
|
-
// // --- Treatment AFTER function execution ---
|
|
6174
|
-
// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
6175
|
-
|
|
6176
|
-
inputObjects.push(result);
|
|
6177
|
-
localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
6178
6939
|
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
},attribute=>isFunction(attribute));
|
|
6940
|
+
|
|
6941
|
+
|
|
6942
|
+
|
|
6943
|
+
class ServerCellLobule{
|
|
6184
6944
|
|
|
6185
|
-
|
|
6186
|
-
|
|
6945
|
+
constructor(){
|
|
6946
|
+
this.serversBag=null;
|
|
6947
|
+
this.objectsBag=null;
|
|
6948
|
+
this.clear();
|
|
6949
|
+
this.clientsConnectionsInfos={};
|
|
6950
|
+
}
|
|
6951
|
+
append(clientId, objs, attributeName="objectsBag"){
|
|
6952
|
+
const self=this;
|
|
6953
|
+
if(!this[attributeName][clientId])
|
|
6954
|
+
this[attributeName][clientId]=[];
|
|
6955
|
+
foreach(objs,obj=>{
|
|
6956
|
+
self[attributeName][clientId].push(obj);
|
|
6957
|
+
},(obj)=>(!contains(this[attributeName][clientId], obj)));
|
|
6958
|
+
}
|
|
6959
|
+
getObjects(clientId, filterFunction=null){
|
|
6960
|
+
if(!filterFunction) return this.objectsBag[clientId];
|
|
6961
|
+
if(!this.objectsBag[clientId]) return null;
|
|
6962
|
+
const results=[];
|
|
6963
|
+
foreach(this.objectsBag[clientId], (obj)=>{
|
|
6964
|
+
results.push(obj);
|
|
6965
|
+
},filterFunction);
|
|
6966
|
+
return results;
|
|
6967
|
+
}
|
|
6968
|
+
getServers(clientId){
|
|
6969
|
+
if(!this.serversBag[clientId]) return null;
|
|
6970
|
+
return this.serversBag[clientId];
|
|
6971
|
+
}
|
|
6972
|
+
clear(clientId=null, attributeName=null){
|
|
6973
|
+
if(!attributeName){
|
|
6974
|
+
if(!clientId){
|
|
6975
|
+
this.serversBag={};
|
|
6976
|
+
this.objectsBag={};
|
|
6977
|
+
}else{
|
|
6978
|
+
this.serversBag[clientId]=[];
|
|
6979
|
+
this.objectsBag[clientId]=[];
|
|
6980
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
6981
|
+
}
|
|
6982
|
+
}else{
|
|
6983
|
+
if(!clientId){
|
|
6984
|
+
this[attributeName]={};
|
|
6985
|
+
}else{
|
|
6986
|
+
this[attributeName][clientId]=[];
|
|
6987
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6990
|
+
|
|
6991
|
+
//DBG
|
|
6992
|
+
if(clientId) lognow("DEBUG : CLEAR() FOR clientId:",clientId);
|
|
6993
|
+
|
|
6994
|
+
return this;
|
|
6995
|
+
}
|
|
6996
|
+
remove(clientId){
|
|
6997
|
+
delete this.serversBag[clientId];
|
|
6998
|
+
delete this.objectsBag[clientId];
|
|
6999
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
7000
|
+
}
|
|
7001
|
+
getClientConnectionInfo(clientId){
|
|
7002
|
+
return this.clientsConnectionsInfos[clientId];
|
|
7003
|
+
}
|
|
7004
|
+
setClientConnectionInfo(clientId, serverWrapper, clientSocket){
|
|
7005
|
+
this.clientsConnectionsInfos[clientId]={serverWrapper:serverWrapper, clientSocket:clientSocket};
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
6187
7008
|
|
|
6188
7009
|
|
|
6189
7010
|
|
|
6190
|
-
window.
|
|
7011
|
+
window.getAORTACServerCell=function(quorumNumber=1, selfOrigin="ws://127.0.0.1:40000", outcomingCellsOrigins=[], model, controller){
|
|
6191
7012
|
//return new AORTACServerNode("node_"+getUUID("short"), selfOrigin, outcomingCellsOrigins, model, controller);
|
|
6192
|
-
if(window.
|
|
7013
|
+
if(window.aortacCServerCellInstance){
|
|
6193
7014
|
// TRACE
|
|
6194
|
-
throw new Error("ERROR : The
|
|
7015
|
+
throw new Error("ERROR : The aortacCServerCellInstance singleton instance already exists. It cannot be instantiated again in the same process. Aborting.");
|
|
6195
7016
|
}
|
|
6196
|
-
window.
|
|
6197
|
-
return window.
|
|
7017
|
+
window.aortacCServerCellInstance=new AORTACServerCell(quorumNumber, selfOrigin, outcomingCellsOrigins, model, controller);
|
|
7018
|
+
return window.aortacCServerCellInstance;
|
|
6198
7019
|
}
|
|
6199
7020
|
|
|
6200
7021
|
|
|
6201
7022
|
|
|
6202
7023
|
|
|
7024
|
+
// ===============================================================================================================================
|
|
7025
|
+
// ===============================================================================================================================
|
|
7026
|
+
// UTILITY METHODS
|
|
6203
7027
|
|
|
6204
7028
|
|
|
7029
|
+
// USAGE EXAMPLE :
|
|
6205
7030
|
|
|
6206
|
-
// ==================================================================================================================
|
|
6207
7031
|
|
|
7032
|
+
// 4 tests only :
|
|
7033
|
+
//class PricingService {
|
|
7034
|
+
// constructor(currency) {
|
|
7035
|
+
// this.currency = currency;
|
|
7036
|
+
// this.callCount = 0;
|
|
7037
|
+
// }
|
|
7038
|
+
//
|
|
7039
|
+
// getPrice(eventId) {
|
|
7040
|
+
// this.callCount++;
|
|
7041
|
+
// return { eventId, amount: 89.99, currency: this.currency };
|
|
7042
|
+
// }
|
|
7043
|
+
//
|
|
7044
|
+
// async updatePrice(eventId, amount) {
|
|
7045
|
+
// await new Promise(r => setTimeout(r, 10)); // simulate async
|
|
7046
|
+
// return { eventId, amount, saved: true };
|
|
7047
|
+
// }
|
|
7048
|
+
//}
|
|
7049
|
+
//
|
|
7050
|
+
//proxyClass(PricingService);
|
|
7051
|
+
//const proxiedClassInstance=new PricingService("CAD");
|
|
7052
|
+
//proxiedClassInstance.currency; // [GET] PricingService.currency{ value: "CAD" }
|
|
7053
|
+
//proxiedClassInstance.currency="USD"; // [SET] PricingService.currency{ value: "USD" }
|
|
7054
|
+
//proxiedClassInstance.getPrice("evt-42"); // [CALL] PricingService.getPrice(){ args: ["evt-42"], result:{...} }
|
|
7055
|
+
//proxiedClassInstance.callCount; // [GET] PricingService.callCount{ value: 1 }
|
|
7056
|
+
//await proxiedClassInstance.updatePrice("evt-42", 120); // [CALL] PricingService.updatePrice(){ args: [...], result:{...} }
|
|
7057
|
+
//restoreNonProxiedClass(PricingService); // undo — new PricingService() works normally again
|
|
6208
7058
|
|
|
6209
|
-
// AORTAC CLIENT
|
|
6210
7059
|
|
|
7060
|
+
window.proxyClass=function(classToProxy,
|
|
7061
|
+
doOnAccess=(className, propertyName, parameter)=>{lognow(`Accessing ${className}.${propertyName}:`,parameter);}){
|
|
6211
7062
|
|
|
7063
|
+
const className=classToProxy.name;
|
|
6212
7064
|
|
|
6213
|
-
|
|
7065
|
+
// Build the Proxy handler
|
|
7066
|
+
const proxyHandler={
|
|
6214
7067
|
|
|
6215
|
-
//
|
|
7068
|
+
// Intercepts: obj.property AND obj.method()
|
|
7069
|
+
get(concernedObject, property, thisArg){
|
|
7070
|
+
const value=Reflect.get(concernedObject, property, thisArg);
|
|
6216
7071
|
|
|
6217
|
-
|
|
7072
|
+
// We skip symbols (ex. Symbol.toPrimitive) — not meaningful to intercept
|
|
7073
|
+
if(typeof(property)!=="string") return value;
|
|
7074
|
+
|
|
7075
|
+
if(typeof(value)==="function"){
|
|
7076
|
+
|
|
7077
|
+
// Return a wrapper so we can intercept args + return value at call time
|
|
7078
|
+
return function(...args){
|
|
7079
|
+
const result=value.apply(concernedObject, args); // `concernedObject` keeps correct `this`
|
|
7080
|
+
|
|
7081
|
+
if(result instanceof Promise){
|
|
7082
|
+
// Async method — intercept after the promise settles
|
|
7083
|
+
return result
|
|
7084
|
+
.then(v=>{ doOnAccess(className, property, { args, result: v }); return v; })
|
|
7085
|
+
.catch(e=>{ doOnAccess(className, property, { args, error: e }); throw e; });
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
// Sync method — intercept immediately
|
|
7089
|
+
doOnAccess(className, property, { args, result }/*(SYNTAX : Equivalent to { args:args, result:result })*/);
|
|
7090
|
+
return result;
|
|
7091
|
+
};
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
// Plain attribute read
|
|
7095
|
+
doOnAccess(className, property, { value });
|
|
7096
|
+
return value;
|
|
7097
|
+
},
|
|
7098
|
+
|
|
7099
|
+
// Intercepts: obj.property=value
|
|
7100
|
+
set(concernedObject, property, valueToSet, thisArg){
|
|
7101
|
+
if(typeof(property)==="string")
|
|
7102
|
+
doOnAccess(className, property, { valueToSet });
|
|
7103
|
+
return Reflect.set(concernedObject, property, valueToSet, thisArg);
|
|
7104
|
+
},
|
|
7105
|
+
};
|
|
7106
|
+
|
|
7107
|
+
// Patch the constructor
|
|
7108
|
+
// Save the original so we can restore later
|
|
7109
|
+
const originalConstructor=classToProxy.prototype.constructor;
|
|
7110
|
+
classToProxy.prototype.originalConstructor=originalConstructor;
|
|
7111
|
+
|
|
7112
|
+
// Override the constructor: it runs first, then we wrap `this` in a Proxy
|
|
7113
|
+
classToProxy.prototype.constructor=function(...args){
|
|
7114
|
+
classToProxy.prototype.originalConstructor.apply(this, args); // run original setup
|
|
7115
|
+
return new Proxy(this, proxyHandler); // return proxy instead of raw `this`
|
|
7116
|
+
};
|
|
7117
|
+
|
|
7118
|
+
// Keep the name and prototype intact so instanceof still works
|
|
7119
|
+
Object.defineProperty(classToProxy.prototype.constructor, "name", { value: className });
|
|
7120
|
+
classToProxy.prototype.constructor.prototype=classToProxy.prototype;
|
|
7121
|
+
|
|
7122
|
+
return classToProxy;
|
|
7123
|
+
};
|
|
7124
|
+
|
|
7125
|
+
// Return a restore function
|
|
7126
|
+
window.restoreNonProxiedClass=function(classToProxy){
|
|
7127
|
+
if(!classToProxy.prototype.originalConstructor){
|
|
7128
|
+
// TRACE
|
|
7129
|
+
lognow("WARN : Class has not been proxied. We do nothing. Concerned class:",classToProxy);
|
|
7130
|
+
return;
|
|
7131
|
+
}
|
|
7132
|
+
classToProxy.prototype.constructor=classToProxy.prototype.originalConstructor;
|
|
7133
|
+
};
|
|
7134
|
+
|
|
7135
|
+
|
|
7136
|
+
|
|
7137
|
+
|
|
7138
|
+
// ******************************************************************
|
|
7139
|
+
|
|
7140
|
+
window.getCurrentLevelConfigForAORTACServerCell=()=>{
|
|
7141
|
+
const allPrototypesConfig=PROTOTYPES_CONFIG.allPrototypes;
|
|
7142
|
+
const levelConfig=allPrototypesConfig.GameLevel;
|
|
7143
|
+
|
|
7144
|
+
const currentLevelPrototypeName=getCurrentLevelPrototypeNameForAORTACServerCell();
|
|
7145
|
+
if(nothing(currentLevelPrototypeName)){
|
|
7146
|
+
// TRACE
|
|
7147
|
+
lognow("WARN : No currentLevelPrototypeName found. Aborting.");
|
|
7148
|
+
return null;
|
|
7149
|
+
}
|
|
7150
|
+
const referenceCurrentLevelConfig=levelConfig[currentLevelPrototypeName];
|
|
7151
|
+
return referenceCurrentLevelConfig;
|
|
7152
|
+
};
|
|
7153
|
+
|
|
7154
|
+
window.getCurrentLevelPrototypeNameForAORTACServerCell=()=>{
|
|
6218
7155
|
|
|
6219
|
-
|
|
7156
|
+
const aortacConfig=GAME_CONFIG.aortac;
|
|
7157
|
+
if(nothing(aortacConfig)){
|
|
7158
|
+
// TRACE
|
|
7159
|
+
lognow("WARN : No AORTAC config, Aborting..");
|
|
7160
|
+
return null;
|
|
7161
|
+
}
|
|
6220
7162
|
|
|
7163
|
+
const allPrototypesConfig=PROTOTYPES_CONFIG.allPrototypes;
|
|
7164
|
+
const levelConfig=allPrototypesConfig.GameLevel;
|
|
7165
|
+
|
|
7166
|
+
let currentLevelPrototypeName=aortacConfig.startingLevelPrototypeName;
|
|
7167
|
+
if(nothing(currentLevelPrototypeName)){
|
|
7168
|
+
currentLevelPrototypeName=getKeyAt(levelConfig,0);
|
|
7169
|
+
// TRACE
|
|
7170
|
+
lognow("WARN : No startingLevelPrototypeName specified in AORTAC config, using first level name as default «"+currentLevelPrototypeName+"».");
|
|
6221
7171
|
}
|
|
6222
7172
|
|
|
6223
|
-
|
|
7173
|
+
return currentLevelPrototypeName;
|
|
7174
|
+
};
|
|
6224
7175
|
|
|
6225
7176
|
|
|
6226
7177
|
|
|
6227
7178
|
|
|
6228
|
-
window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
6229
|
-
//return new AORTACClient("client_"+getUUID(), serverNodeOrigin, model, view, isNodeContext);
|
|
6230
|
-
return new AORTACClientCell(serverNodeOrigin, model, view, isNodeContext);
|
|
6231
|
-
}
|
|
6232
7179
|
|
|
7180
|
+
//
|
|
7181
|
+
//// Public static hydration method :
|
|
7182
|
+
//window.ao=(incompleteModelObjectToDecorate)=>{
|
|
7183
|
+
//
|
|
7184
|
+
// const localServerCell=window.aortacCServerCellInstance;
|
|
7185
|
+
// if(!localServerCell){
|
|
7186
|
+
// // TRACE
|
|
7187
|
+
// lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
7188
|
+
// return incompleteModelObjectToDecorate;
|
|
7189
|
+
// }
|
|
7190
|
+
//
|
|
7191
|
+
// const liveModelObjects=localServerCell.liveModelObjects;
|
|
7192
|
+
//
|
|
7193
|
+
// // First we clone the object, for its attriutes values information :
|
|
7194
|
+
// const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
7195
|
+
//
|
|
7196
|
+
// // Then we replace all its methods :
|
|
7197
|
+
// foreach(clonedObject, (method, methodName)=>{
|
|
7198
|
+
// clonedObject[methodName]=new Proxy(method,
|
|
7199
|
+
// {
|
|
7200
|
+
// apply: function(methodToEnhance, thisArg, argumentsList){
|
|
7201
|
+
//
|
|
7202
|
+
// const inputObjects=[thisArg];
|
|
7203
|
+
// inputObjects.push(...argumentsList);
|
|
7204
|
+
// localServerCell.collectDependencies(inputObjects);
|
|
7205
|
+
//
|
|
7206
|
+
//// // --- Treatment BEFORE function execution ---
|
|
7207
|
+
//// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
7208
|
+
//
|
|
7209
|
+
// // Call the original function(target) with its intended "this" context (thisArg)
|
|
7210
|
+
// // and arguments (argumentsList) using Reflect.apply
|
|
7211
|
+
// const result=Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
7212
|
+
//
|
|
7213
|
+
//// // --- Treatment AFTER function execution ---
|
|
7214
|
+
//// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
7215
|
+
//
|
|
7216
|
+
// inputObjects.push(result);
|
|
7217
|
+
// localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
7218
|
+
//
|
|
7219
|
+
// return result;
|
|
7220
|
+
// },
|
|
7221
|
+
// }
|
|
7222
|
+
// );
|
|
7223
|
+
// },attribute=>isFunction(attribute));
|
|
7224
|
+
//
|
|
7225
|
+
// return clonedObject;
|
|
7226
|
+
//};
|
|
6233
7227
|
|
|
6234
7228
|
|
|
6235
7229
|
|
|
@@ -6945,7 +7939,7 @@ WebsocketImplementation={
|
|
|
6945
7939
|
},
|
|
6946
7940
|
|
|
6947
7941
|
|
|
6948
|
-
/*
|
|
7942
|
+
/*public*//*static*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
|
|
6949
7943
|
|
|
6950
7944
|
const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
|
|
6951
7945
|
|
|
@@ -7142,12 +8136,11 @@ WebsocketImplementation={
|
|
|
7142
8136
|
|
|
7143
8137
|
|
|
7144
8138
|
|
|
7145
|
-
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
|
|
8139
|
+
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, doOnConnectionLost=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
|
|
7146
8140
|
|
|
7147
8141
|
const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
|
|
7148
8142
|
|
|
7149
8143
|
|
|
7150
|
-
|
|
7151
8144
|
if(typeof(https)==="undefined"){
|
|
7152
8145
|
// TRACE
|
|
7153
8146
|
console.log("«https» SERVER library not called yet, calling it now.");
|
|
@@ -7239,8 +8232,6 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7239
8232
|
|
|
7240
8233
|
const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
|
|
7241
8234
|
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
8235
|
let listenableServer;
|
|
7245
8236
|
if(sslOptions){
|
|
7246
8237
|
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
@@ -7261,7 +8252,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7261
8252
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
7262
8253
|
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
7263
8254
|
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
7264
|
-
});
|
|
8255
|
+
}, doOnConnectionLost);
|
|
7265
8256
|
|
|
7266
8257
|
|
|
7267
8258
|
server.onFinalize((serverParam)=>{
|
|
@@ -7276,15 +8267,13 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7276
8267
|
// TRACE
|
|
7277
8268
|
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
7278
8269
|
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
8270
|
|
|
7282
8271
|
|
|
7283
8272
|
return server;
|
|
7284
8273
|
}
|
|
7285
8274
|
|
|
7286
8275
|
|
|
7287
|
-
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
|
|
8276
|
+
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, doOnConnectionLost=null,
|
|
7288
8277
|
/*OPTIONAL*/portParam,
|
|
7289
8278
|
/*OPTIONAL*/certPathParam,
|
|
7290
8279
|
/*OPTIONAL*/keyPathParam){
|
|
@@ -7337,13 +8326,11 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7337
8326
|
|
|
7338
8327
|
|
|
7339
8328
|
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
8329
|
const aotraNodeServer={config:serverConfig};
|
|
7343
8330
|
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
7344
8331
|
|
|
7345
8332
|
if(isHashAsked){
|
|
7346
|
-
// We
|
|
8333
|
+
// We instantiate a temporary persister just to read the key hash file:
|
|
7347
8334
|
const persister=getPersister("./");
|
|
7348
8335
|
let persisterIdSplits=persisterId.split("@");
|
|
7349
8336
|
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
@@ -7426,9 +8413,9 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7426
8413
|
}
|
|
7427
8414
|
}
|
|
7428
8415
|
|
|
7429
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
8416
|
+
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, doOnConnectionLost, sslOptions);
|
|
8417
|
+
|
|
7430
8418
|
|
|
7431
|
-
|
|
7432
8419
|
return aotraNodeServer;
|
|
7433
8420
|
};
|
|
7434
8421
|
|
|
@@ -7491,7 +8478,10 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7491
8478
|
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
7492
8479
|
//
|
|
7493
8480
|
//
|
|
7494
|
-
// },
|
|
8481
|
+
// },
|
|
8482
|
+
// // On client connection lost :
|
|
8483
|
+
// (clientId)=>{lognow(`INFO : Connection to client id «${clientId}» was lost.`);},
|
|
8484
|
+
// portParam,certPathParam,keyPathParam);
|
|
7495
8485
|
//
|
|
7496
8486
|
//
|
|
7497
8487
|
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
@@ -7959,8 +8949,8 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
7959
8949
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
7960
8950
|
socketToServerClientInstance.onConnectionToServer(()=>{
|
|
7961
8951
|
if(doOnServerConnection){
|
|
7962
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance]);
|
|
7963
|
-
else doOnServerConnection(socketToServerClientInstance);
|
|
8952
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance, aotraClient]);
|
|
8953
|
+
else doOnServerConnection(socketToServerClientInstance, aotraClient);
|
|
7964
8954
|
}
|
|
7965
8955
|
|
|
7966
8956
|
|
|
@@ -8057,8 +9047,9 @@ class ClientReceptionEntryPoint{
|
|
|
8057
9047
|
|
|
8058
9048
|
|
|
8059
9049
|
// We check if the message matches the required message type :
|
|
8060
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
8061
|
-
&&
|
|
9050
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
9051
|
+
&& dataWrapped.data
|
|
9052
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
8062
9053
|
return;
|
|
8063
9054
|
}
|
|
8064
9055
|
|
|
@@ -8082,8 +9073,12 @@ class ClientReceptionEntryPoint{
|
|
|
8082
9073
|
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
8083
9074
|
remove(clientReceptionEntryPoints, this);
|
|
8084
9075
|
|
|
8085
|
-
if(this.doOnIncomingMessage)
|
|
8086
|
-
|
|
9076
|
+
if(this.doOnIncomingMessage){
|
|
9077
|
+
|
|
9078
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
9079
|
+
|
|
9080
|
+
this.doOnIncomingMessage(dataRestored, clientSocket);
|
|
9081
|
+
}
|
|
8087
9082
|
|
|
8088
9083
|
}
|
|
8089
9084
|
|
|
@@ -8126,7 +9121,9 @@ class ClientInstance{
|
|
|
8126
9121
|
// I-
|
|
8127
9122
|
//
|
|
8128
9123
|
self.receive(channelNameForResponse, doOnIncomingMessageForResponse,
|
|
8129
|
-
|
|
9124
|
+
{messageId:messageId, destroyListenerAfterReceiving:true},
|
|
9125
|
+
clientsRoomsTag, listenerId,
|
|
9126
|
+
);
|
|
8130
9127
|
//
|
|
8131
9128
|
|
|
8132
9129
|
return resultPromise;
|
|
@@ -8141,7 +9138,7 @@ class ClientInstance{
|
|
|
8141
9138
|
}
|
|
8142
9139
|
|
|
8143
9140
|
// II-
|
|
8144
|
-
receive(channelNameParam, doOnIncomingMessage,
|
|
9141
|
+
receive(channelNameParam, doOnIncomingMessage, listenerConfig={destroyListenerAfterReceiving:false, listenerMessageType:null}, clientsRoomsTag=null, entryPointId=null){
|
|
8145
9142
|
const self=this;
|
|
8146
9143
|
|
|
8147
9144
|
// // DBG
|
|
@@ -8181,8 +9178,11 @@ class ClientInstance{
|
|
|
8181
9178
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8182
9179
|
|
|
8183
9180
|
// Channel information is stored in exchanged data :
|
|
8184
|
-
let dataWrapped={channelName:channelNameParam, data:data};
|
|
8185
|
-
|
|
9181
|
+
let dataWrapped=saveClassesInformation({channelName:channelNameParam, data:data});
|
|
9182
|
+
|
|
9183
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
9184
|
+
// (or else we'll have TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object)
|
|
9185
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
8186
9186
|
dataWrapped=stringifyObject(dataWrapped);
|
|
8187
9187
|
|
|
8188
9188
|
// TODO : FIXME : Use one single interface !
|
|
@@ -8314,8 +9314,9 @@ class ServerReceptionEntryPoint{
|
|
|
8314
9314
|
if(!isClientInRoom) return;
|
|
8315
9315
|
|
|
8316
9316
|
// We check if the message matches the required message type :
|
|
8317
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
8318
|
-
|
|
9317
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
9318
|
+
&& dataWrapped.data
|
|
9319
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
8319
9320
|
return;
|
|
8320
9321
|
}
|
|
8321
9322
|
|
|
@@ -8323,7 +9324,11 @@ class ServerReceptionEntryPoint{
|
|
|
8323
9324
|
// // DBG
|
|
8324
9325
|
// lognow("(SERVER) this.doOnIncomingMessage:");
|
|
8325
9326
|
|
|
8326
|
-
|
|
9327
|
+
const dataLocal=dataWrapped.data;
|
|
9328
|
+
|
|
9329
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
9330
|
+
|
|
9331
|
+
this.doOnIncomingMessage(dataRestored, clientSocketParam);
|
|
8327
9332
|
}
|
|
8328
9333
|
|
|
8329
9334
|
}
|
|
@@ -8371,13 +9376,10 @@ class NodeServerInstance{
|
|
|
8371
9376
|
});
|
|
8372
9377
|
}
|
|
8373
9378
|
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
9379
|
// TODO : DEVELOP...
|
|
8377
9380
|
//sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
8378
9381
|
//}
|
|
8379
9382
|
|
|
8380
|
-
|
|
8381
9383
|
receive(channelNameParam, doOnIncomingMessage, listenerConfig=null, clientsRoomsTag=null){
|
|
8382
9384
|
|
|
8383
9385
|
// DBG
|
|
@@ -8425,14 +9427,16 @@ class NodeServerInstance{
|
|
|
8425
9427
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8426
9428
|
|
|
8427
9429
|
// Channel information is stored in exchanged data :
|
|
8428
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8429
|
-
|
|
8430
|
-
|
|
9430
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
9431
|
+
|
|
9432
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
9433
|
+
// (or else we'll have TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object)
|
|
9434
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
8431
9435
|
dataWrapped=stringifyObject(dataWrapped);
|
|
8432
9436
|
|
|
8433
9437
|
// TODO : FIXME : Use one single interface !
|
|
8434
9438
|
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8435
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
9439
|
+
else clientSocket.emit(channelName, dataWrapped);
|
|
8436
9440
|
|
|
8437
9441
|
});
|
|
8438
9442
|
|
|
@@ -8452,12 +9456,12 @@ class NodeServerInstance{
|
|
|
8452
9456
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8453
9457
|
|
|
8454
9458
|
// Channel information is stored in exchanged data :
|
|
8455
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8456
|
-
dataWrapped=stringifyObject(dataWrapped);
|
|
9459
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
8457
9460
|
|
|
8458
|
-
|
|
8459
|
-
//
|
|
8460
|
-
|
|
9461
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
9462
|
+
// (or else we'll have TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object)
|
|
9463
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
9464
|
+
dataWrapped=stringifyObject(dataWrapped);
|
|
8461
9465
|
|
|
8462
9466
|
|
|
8463
9467
|
// TODO : FIXME : Use one single interface !
|
|
@@ -8470,8 +9474,8 @@ class NodeServerInstance{
|
|
|
8470
9474
|
return this;
|
|
8471
9475
|
}
|
|
8472
9476
|
|
|
8473
|
-
|
|
8474
|
-
onConnectionToClient(doOnConnection){
|
|
9477
|
+
|
|
9478
|
+
onConnectionToClient(doOnConnection, doOnConnectionLost=null){
|
|
8475
9479
|
const self=this;
|
|
8476
9480
|
|
|
8477
9481
|
|
|
@@ -8480,7 +9484,6 @@ class NodeServerInstance{
|
|
|
8480
9484
|
|
|
8481
9485
|
// DBG
|
|
8482
9486
|
console.log("SERVER : ON CONNECTION !");
|
|
8483
|
-
|
|
8484
9487
|
|
|
8485
9488
|
const clientId="autogeneratedid_"+getUUID();
|
|
8486
9489
|
|
|
@@ -8531,6 +9534,9 @@ class NodeServerInstance{
|
|
|
8531
9534
|
|
|
8532
9535
|
|
|
8533
9536
|
clearInterval(clientSocket.stateCheckInterval);
|
|
9537
|
+
|
|
9538
|
+
if(doOnConnectionLost) doOnConnectionLost(clientSocket.clientId);
|
|
9539
|
+
|
|
8534
9540
|
return;
|
|
8535
9541
|
}
|
|
8536
9542
|
|
|
@@ -8589,7 +9595,71 @@ class NodeServerInstance{
|
|
|
8589
9595
|
|
|
8590
9596
|
|
|
8591
9597
|
|
|
9598
|
+
window.saveClassesInformation=(obj, visitedObjects=[])=>{
|
|
9599
|
+
|
|
9600
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
9601
|
+
|
|
9602
|
+
if(contains(visitedObjects, obj)) return obj;
|
|
9603
|
+
visitedObjects.push(obj);
|
|
9604
|
+
|
|
9605
|
+
const className=getClassName(obj);
|
|
9606
|
+
if(className!="Object"){
|
|
9607
|
+
// We do the save :
|
|
9608
|
+
obj[attributeClassName]=className;
|
|
9609
|
+
|
|
9610
|
+
// DBG
|
|
9611
|
+
lognow("0 saving class name : :",obj);
|
|
9612
|
+
|
|
9613
|
+
}
|
|
9614
|
+
|
|
9615
|
+
foreach(obj, (attr, attrName)=>{
|
|
9616
|
+
const className=getClassName(attr);
|
|
9617
|
+
|
|
9618
|
+
// DBG
|
|
9619
|
+
if(attrName=="position") lognow("1 attr:",attr);
|
|
9620
|
+
|
|
9621
|
+
if(className!="Object"){
|
|
9622
|
+
saveClassesInformation(attr, visitedObjects);
|
|
9623
|
+
}
|
|
9624
|
+
|
|
9625
|
+
// DBG
|
|
9626
|
+
if(attrName=="position") lognow("2 attr:",attr);
|
|
9627
|
+
|
|
9628
|
+
},(attr, attrName)=>
|
|
9629
|
+
attr &&
|
|
9630
|
+
// DEBUG ONLY
|
|
9631
|
+
(attrName=="position" || (
|
|
9632
|
+
isObject(attr) && !attr[attributeClassName]
|
|
9633
|
+
))
|
|
9634
|
+
);
|
|
9635
|
+
|
|
9636
|
+
return obj;
|
|
9637
|
+
};
|
|
9638
|
+
|
|
9639
|
+
|
|
9640
|
+
window.restoreClassesInformation=(obj, visitedObjects=[])=>{
|
|
9641
|
+
|
|
9642
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
9643
|
+
|
|
9644
|
+
if(contains(visitedObjects, obj)) return obj;
|
|
9645
|
+
visitedObjects.push(obj);
|
|
9646
|
+
|
|
9647
|
+
const className=obj[attributeClassName];
|
|
9648
|
+
if(className){
|
|
9649
|
+
// We do the restore :
|
|
9650
|
+
const blankInstance=instantiate(className);
|
|
9651
|
+
obj=Object.assign(blankInstance, obj);
|
|
9652
|
+
}
|
|
9653
|
+
|
|
9654
|
+
foreach(obj, (attr, attrName)=>{
|
|
9655
|
+
const className=attr[attributeClassName];
|
|
9656
|
+
if(className!="Object"){
|
|
9657
|
+
obj[attrName]=restoreClassesInformation(attr, visitedObjects);
|
|
9658
|
+
}
|
|
9659
|
+
},attr=>attr && ((isObject(attr) && attr[attributeClassName]) || isArray(attr)) );
|
|
8592
9660
|
|
|
9661
|
+
return obj;
|
|
9662
|
+
};
|
|
8593
9663
|
|
|
8594
9664
|
|
|
8595
9665
|
|