aotrautils-srv 0.0.1840 → 0.0.1846
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 +1450 -373
- 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:06:15)»*/
|
|
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,14 +4152,27 @@ 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
|
|
4134
|
-
|
|
4155
|
+
lognow(`ERROR : CAUTION : «${className}» class does not seem to be registered in the global object. Cannot instantiate it.`);
|
|
4156
|
+
|
|
4157
|
+
// DEBUG
|
|
4158
|
+
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4159
|
+
newObj=eval("new "+className+"();");
|
|
4160
|
+
|
|
4135
4161
|
}
|
|
4136
4162
|
}else{
|
|
4137
|
-
//
|
|
4138
|
-
lognow(`ERROR : CAUTION : «${className}» class does not seem to be accessible in the globalThis nor window object, and global object does not exist. Cannot
|
|
4139
|
-
|
|
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.`);
|
|
4165
|
+
|
|
4166
|
+
// DEBUG
|
|
4167
|
+
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4168
|
+
newObj=eval("new "+className+"();");
|
|
4169
|
+
|
|
4140
4170
|
}
|
|
4171
|
+
|
|
4172
|
+
// WORKAROUND
|
|
4173
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4174
|
+
|
|
4175
|
+
return newObj;
|
|
4141
4176
|
}
|
|
4142
4177
|
}
|
|
4143
4178
|
|
|
@@ -4147,21 +4182,21 @@ window.instanciate=function(className=null){
|
|
|
4147
4182
|
// newObj=Reflect.construct(classInWindow);
|
|
4148
4183
|
// }catch(e1){
|
|
4149
4184
|
//// // TRACE
|
|
4150
|
-
//// console.log("WARN : Could not
|
|
4185
|
+
//// console.log("WARN : Could not instantiate class «"+className+"». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4151
4186
|
// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4152
4187
|
// newObj=eval("new "+className+"();");
|
|
4153
4188
|
// }
|
|
4154
4189
|
// }catch(e2){
|
|
4155
4190
|
//// // TRACE
|
|
4156
|
-
//// 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 !)");
|
|
4157
4192
|
// try{
|
|
4158
4193
|
//// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4159
4194
|
//// newObj=eval("new window."+className+"();");
|
|
4160
4195
|
// newObj=Reflect.construct(classInWindow);
|
|
4161
4196
|
// }catch(e3){
|
|
4162
4197
|
//// // TRACE
|
|
4163
|
-
//// console.log("ERROR : Could not
|
|
4164
|
-
//// 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 !)");
|
|
4165
4200
|
// // TODO : FIXME : This is the most performance-costing fallback :
|
|
4166
4201
|
// try{
|
|
4167
4202
|
// if(classInWindow){
|
|
@@ -4173,7 +4208,7 @@ window.instanciate=function(className=null){
|
|
|
4173
4208
|
// }
|
|
4174
4209
|
// }catch(e4){
|
|
4175
4210
|
//// // TRACE
|
|
4176
|
-
//// console.log("ERROR : Could not
|
|
4211
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with prototype affectation.");
|
|
4177
4212
|
//// console.log("ERROR : Returning plain object since all instantiation methods have failed for class «"+className+"».");
|
|
4178
4213
|
// }
|
|
4179
4214
|
// }
|
|
@@ -4181,17 +4216,17 @@ window.instanciate=function(className=null){
|
|
|
4181
4216
|
|
|
4182
4217
|
// NEW :
|
|
4183
4218
|
newObj=new classInWindow();
|
|
4219
|
+
|
|
4220
|
+
// WORKAROUND
|
|
4221
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4222
|
+
|
|
4184
4223
|
return newObj;
|
|
4185
4224
|
};
|
|
4186
4225
|
|
|
4187
4226
|
|
|
4188
4227
|
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
4228
|
JSON.stringifyDecycle=function(obj){
|
|
4194
|
-
let decycled=JSON.decycle(obj,null,
|
|
4229
|
+
let decycled=JSON.decycle(obj,null,CLASSNAME_ATTRIBUTE_NAME);
|
|
4195
4230
|
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
4196
4231
|
return stringifyObject(decycled);
|
|
4197
4232
|
}
|
|
@@ -4199,6 +4234,8 @@ JSON.stringifyDecycle=function(obj){
|
|
|
4199
4234
|
|
|
4200
4235
|
JSON.parseRecycle=function(str){
|
|
4201
4236
|
|
|
4237
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
4238
|
+
|
|
4202
4239
|
const parsedDecycled=parseJSON(str);
|
|
4203
4240
|
let restoreClass=function(objParam){
|
|
4204
4241
|
|
|
@@ -4208,11 +4245,11 @@ JSON.parseRecycle=function(str){
|
|
|
4208
4245
|
if(isArray(objParam)){
|
|
4209
4246
|
newObj=[];
|
|
4210
4247
|
}else{
|
|
4211
|
-
let className=objParam[
|
|
4248
|
+
let className=objParam[attributeClassName];
|
|
4212
4249
|
if(!className || isNativeClassName(className)){
|
|
4213
4250
|
newObj= {};
|
|
4214
4251
|
}else{
|
|
4215
|
-
newObj=
|
|
4252
|
+
newObj=instantiate(className);
|
|
4216
4253
|
}
|
|
4217
4254
|
}
|
|
4218
4255
|
|
|
@@ -4242,8 +4279,11 @@ JSON.parseRecycle=function(str){
|
|
|
4242
4279
|
,(itemOrAttr)=>{ return !isFunction(itemOrAttr); }
|
|
4243
4280
|
);
|
|
4244
4281
|
|
|
4245
|
-
|
|
4246
|
-
|
|
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];
|
|
4247
4287
|
}
|
|
4248
4288
|
|
|
4249
4289
|
};
|
|
@@ -4261,6 +4301,7 @@ JSON.parseRecycle=function(str){
|
|
|
4261
4301
|
|
|
4262
4302
|
|
|
4263
4303
|
window.clone=function(obj){
|
|
4304
|
+
if(obj==null) return null;
|
|
4264
4305
|
// OLD :
|
|
4265
4306
|
const str=JSON.stringifyDecycle(obj);
|
|
4266
4307
|
const newObj=JSON.parseRecycle(str);
|
|
@@ -4733,7 +4774,7 @@ window.isFlatMap=function(obj){
|
|
|
4733
4774
|
|
|
4734
4775
|
window.getAsFlatStructure=function(rawObject
|
|
4735
4776
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4736
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4777
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4737
4778
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4738
4779
|
){
|
|
4739
4780
|
// Flattening :
|
|
@@ -4784,7 +4825,7 @@ function flattenGraph(root, UUID_ATTR_NAME, CLASSNAME_ATTR_NAME, POINTER_TO_ATTR
|
|
|
4784
4825
|
|
|
4785
4826
|
window.getAsTreeStructure=function(oldMap, keepClassName=false
|
|
4786
4827
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4787
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4828
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4788
4829
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4789
4830
|
){
|
|
4790
4831
|
|
|
@@ -4806,7 +4847,7 @@ window.restoreGraph=(flatData, keepClassName=false, UUID_ATTR_NAME, CLASSNAME_AT
|
|
|
4806
4847
|
|
|
4807
4848
|
const objData = flatData[id];
|
|
4808
4849
|
const className = objData[CLASSNAME_ATTR_NAME];
|
|
4809
|
-
const newInstance =
|
|
4850
|
+
const newInstance = instantiate(className);
|
|
4810
4851
|
|
|
4811
4852
|
restoredObjects[id]=newInstance; // Assign ID early to handle circular references
|
|
4812
4853
|
|
|
@@ -5171,7 +5212,7 @@ window.getMonoThreadedTimeout=function(actuator=null){
|
|
|
5171
5212
|
|
|
5172
5213
|
// ======================== Routine ========================
|
|
5173
5214
|
|
|
5174
|
-
window.MonoThreadedRoutine=class {
|
|
5215
|
+
window.MonoThreadedRoutine=class MonoThreadedRoutine{
|
|
5175
5216
|
constructor(actuator,refreshMillis=null,startDependsOnParentOnly=false){
|
|
5176
5217
|
|
|
5177
5218
|
this.actuator=actuator;
|
|
@@ -5181,15 +5222,21 @@ window.MonoThreadedRoutine=class {
|
|
|
5181
5222
|
this.time=getNow();
|
|
5182
5223
|
this.refreshMillis=refreshMillis;
|
|
5183
5224
|
|
|
5184
|
-
this.
|
|
5225
|
+
this.managedTimeFactor=1;
|
|
5185
5226
|
// Three-states : started, paused, stopped.
|
|
5186
5227
|
|
|
5187
5228
|
}
|
|
5188
5229
|
|
|
5189
|
-
|
|
5190
|
-
this
|
|
5230
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5231
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5191
5232
|
return this;
|
|
5192
5233
|
}
|
|
5234
|
+
|
|
5235
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5236
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5237
|
+
return this;
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5193
5240
|
isStarted(){
|
|
5194
5241
|
return this.started || this.startDependsOnParentOnly;
|
|
5195
5242
|
}
|
|
@@ -5226,7 +5273,7 @@ window.MonoThreadedRoutine=class {
|
|
|
5226
5273
|
if(!this.isStarted() || this.paused) return;
|
|
5227
5274
|
// Looping index with a delay :
|
|
5228
5275
|
|
|
5229
|
-
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5276
|
+
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor);
|
|
5230
5277
|
if(!this.refreshMillis || delayHasPassed){
|
|
5231
5278
|
if(this.refreshMillis && delayHasPassed){
|
|
5232
5279
|
this.time=getNow();
|
|
@@ -5236,7 +5283,8 @@ window.MonoThreadedRoutine=class {
|
|
|
5236
5283
|
this.stop(args);
|
|
5237
5284
|
return;
|
|
5238
5285
|
}
|
|
5239
|
-
if(this.actuator.doOnEachStep)
|
|
5286
|
+
if(this.actuator.doOnEachStep)
|
|
5287
|
+
this.actuator.doOnEachStep(args);
|
|
5240
5288
|
}
|
|
5241
5289
|
|
|
5242
5290
|
}
|
|
@@ -5265,19 +5313,27 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5265
5313
|
this.time=getNow();
|
|
5266
5314
|
this.refreshMillis=refreshMillis;
|
|
5267
5315
|
|
|
5268
|
-
this.
|
|
5316
|
+
this.managedTimeFactor=1;
|
|
5317
|
+
|
|
5269
5318
|
|
|
5270
5319
|
// Three-states : started, paused, stopped.
|
|
5271
5320
|
}
|
|
5272
|
-
|
|
5273
|
-
|
|
5321
|
+
|
|
5322
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5323
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5324
|
+
return this;
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5328
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5274
5329
|
|
|
5275
5330
|
// We recalculate total time-dependent values :
|
|
5276
|
-
this.totalTimeMillis=this.totalTimeMillis*this.
|
|
5331
|
+
this.totalTimeMillis=this.totalTimeMillis*this.managedTimeFactor;
|
|
5277
5332
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.actualValue))/this.totalTimeMillis);
|
|
5278
5333
|
|
|
5279
5334
|
return this;
|
|
5280
5335
|
}
|
|
5336
|
+
|
|
5281
5337
|
isStarted(){
|
|
5282
5338
|
return this.started || this.startDependsOnParentOnly;
|
|
5283
5339
|
}
|
|
@@ -5289,7 +5345,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5289
5345
|
setNewGoal(goalValue, refreshMillisParam=null, totalTimeMillis=null){
|
|
5290
5346
|
if(Math.round(this.value)===Math.round(goalValue)) return;
|
|
5291
5347
|
if(refreshMillisParam) this.refreshMillis=refreshMillisParam;
|
|
5292
|
-
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.
|
|
5348
|
+
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.managedTimeFactor;
|
|
5293
5349
|
|
|
5294
5350
|
this.goalValue=goalValue;
|
|
5295
5351
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.value))/this.totalTimeMillis);
|
|
@@ -5376,7 +5432,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5376
5432
|
}
|
|
5377
5433
|
|
|
5378
5434
|
hasDelayPassed(){
|
|
5379
|
-
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5435
|
+
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor));
|
|
5380
5436
|
}
|
|
5381
5437
|
|
|
5382
5438
|
|
|
@@ -5402,7 +5458,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5402
5458
|
|
|
5403
5459
|
|
|
5404
5460
|
|
|
5405
|
-
/*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:06:15)»*/
|
|
5406
5462
|
/*-----------------------------------------------------------------------------*/
|
|
5407
5463
|
|
|
5408
5464
|
|
|
@@ -5548,11 +5604,383 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
5548
5604
|
|
|
5549
5605
|
|
|
5550
5606
|
|
|
5551
|
-
/*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:06:15)»*/
|
|
5552
5608
|
/*-----------------------------------------------------------------------------*/
|
|
5553
5609
|
|
|
5554
5610
|
|
|
5555
|
-
/* ## 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)
|
|
5556
5984
|
*
|
|
5557
5985
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
5558
5986
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -5585,8 +6013,8 @@ if(typeof(window)==="undefined") window=global;
|
|
|
5585
6013
|
|
|
5586
6014
|
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
|
5587
6015
|
|
|
5588
|
-
// New implementation :
|
|
5589
6016
|
AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS=2000;
|
|
6017
|
+
|
|
5590
6018
|
class AORTACServerCell{
|
|
5591
6019
|
|
|
5592
6020
|
constructor(quorumNumber=1, selfOrigin, outcomingCellsOrigins, model, controller, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
@@ -5599,14 +6027,14 @@ class AORTACServerCell{
|
|
|
5599
6027
|
const infos=splitURL(this.selfOrigin);
|
|
5600
6028
|
|
|
5601
6029
|
//DBG
|
|
5602
|
-
lognow("infos:::::::::::::",infos);
|
|
6030
|
+
lognow("(SERVER) infos:::::::::::::",infos);
|
|
5603
6031
|
|
|
5604
6032
|
this.protocol=nonull(infos.protocol,"ws");
|
|
5605
6033
|
this.host=nonull(infos.host,"localhost");
|
|
5606
6034
|
this.port=nonull(infos.port,"30000");
|
|
5607
6035
|
this.sslConfig=sslConfig;
|
|
5608
6036
|
|
|
5609
|
-
this.
|
|
6037
|
+
this.serverWrapper=null;
|
|
5610
6038
|
this.incomingCells={};
|
|
5611
6039
|
|
|
5612
6040
|
this.outcomingCellsOrigins=outcomingCellsOrigins;
|
|
@@ -5624,20 +6052,26 @@ class AORTACServerCell{
|
|
|
5624
6052
|
};
|
|
5625
6053
|
|
|
5626
6054
|
this.cellsOverview={};
|
|
6055
|
+
this.numberOfPartitions=null;
|
|
6056
|
+
this.partitionId=null;
|
|
6057
|
+
|
|
6058
|
+
this.isServerCellQuorumLastCell=false;
|
|
5627
6059
|
|
|
5628
6060
|
this.startTime=null;
|
|
5629
6061
|
|
|
5630
6062
|
this.quorumIsReachedSequenceIsInitiated=false;
|
|
5631
6063
|
// this.quorumCells=null; // (CAUTION : only the latest ready cell when quorum is reached has this attribute populated)
|
|
5632
6064
|
|
|
5633
|
-
this.
|
|
6065
|
+
this.lobuleZone=new ServerCellLobule();
|
|
6066
|
+
|
|
6067
|
+
this.clients={};
|
|
5634
6068
|
|
|
5635
6069
|
}
|
|
5636
6070
|
|
|
5637
6071
|
start(){
|
|
5638
6072
|
|
|
5639
6073
|
this.startTime=getNow();
|
|
5640
|
-
this.
|
|
6074
|
+
this.serverWrapper=this.launchServerForIncomingConnections();
|
|
5641
6075
|
|
|
5642
6076
|
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
5643
6077
|
// TRACE
|
|
@@ -5654,37 +6088,72 @@ class AORTACServerCell{
|
|
|
5654
6088
|
return this;
|
|
5655
6089
|
}
|
|
5656
6090
|
|
|
6091
|
+
|
|
6092
|
+
// ===================================================================================================
|
|
6093
|
+
// SERVER CELL CORE
|
|
6094
|
+
// ===================================================================================================
|
|
6095
|
+
|
|
5657
6096
|
/*private*/launchServerForIncomingConnections(){
|
|
5658
6097
|
const self=this;
|
|
5659
6098
|
|
|
5660
|
-
const
|
|
6099
|
+
const serverWrapper=initNodeServerInfrastructureWrapper(
|
|
5661
6100
|
// On each client connection :
|
|
5662
6101
|
null,
|
|
5663
6102
|
// On client finalization :
|
|
5664
6103
|
function(server){
|
|
5665
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
|
+
|
|
5666
6111
|
// On-receive message listeners handling ;
|
|
5667
|
-
foreach(
|
|
5668
|
-
server.receive(channelName, (message, clientSocket)=>{
|
|
5669
|
-
self.executeListeners("incoming",channelName, message, clientSocket);
|
|
5670
|
-
});
|
|
5671
|
-
});
|
|
5672
|
-
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{
|
|
6112
|
+
foreach(channelsNames, channelName=>{
|
|
5673
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
|
+
|
|
5674
6123
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
5675
6124
|
});
|
|
5676
6125
|
});
|
|
5677
6126
|
|
|
5678
6127
|
|
|
5679
|
-
// HANDLE HELLO SEQUENCE
|
|
5680
|
-
self.
|
|
6128
|
+
// HANDLE SERVER CELL HELLO SEQUENCE
|
|
6129
|
+
self.handleServerCellHelloSequence();
|
|
6130
|
+
|
|
6131
|
+
|
|
6132
|
+
// ******************************************
|
|
6133
|
+
// CLIENT
|
|
5681
6134
|
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
6135
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
6136
|
+
self.handleClientHelloSequence();
|
|
6137
|
+
|
|
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;
|
|
5685
6151
|
}
|
|
5686
6152
|
|
|
5687
6153
|
/*private*/probeOutcomingNetwork(){
|
|
6154
|
+
|
|
6155
|
+
const self=this;
|
|
6156
|
+
|
|
5688
6157
|
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
5689
6158
|
let numberOfConnectedOutcomingServers=0;
|
|
5690
6159
|
foreach(this.outcomingCells, outcomingCell=>{
|
|
@@ -5706,7 +6175,6 @@ class AORTACServerCell{
|
|
|
5706
6175
|
return;
|
|
5707
6176
|
}
|
|
5708
6177
|
|
|
5709
|
-
const self=this;
|
|
5710
6178
|
|
|
5711
6179
|
// We try to connect to all outcoming cells :
|
|
5712
6180
|
foreach(this.outcomingCells, (outcomingCell, outcomingCellOrigin)=>{
|
|
@@ -5718,20 +6186,29 @@ class AORTACServerCell{
|
|
|
5718
6186
|
|
|
5719
6187
|
const clientInstance=initClient(true, false, (socketToServerClientInstance)=>{
|
|
5720
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
|
+
|
|
5721
6195
|
// On-receive message listeners handling ;
|
|
5722
|
-
foreach(
|
|
6196
|
+
foreach(channelsNames, channelName=>{
|
|
5723
6197
|
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
6198
|
+
|
|
6199
|
+
// DBG
|
|
6200
|
+
lognow("DEBUG : Server cell receives message from outcoming...");
|
|
6201
|
+
|
|
5724
6202
|
self.executeListeners("outcoming",channelName, message, clientSocket);
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
6203
|
+
|
|
6204
|
+
// DBG
|
|
6205
|
+
lognow("DEBUG : Server cell receives message from both (out)...");
|
|
6206
|
+
|
|
5729
6207
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
5730
6208
|
});
|
|
5731
6209
|
});
|
|
5732
|
-
|
|
5733
6210
|
|
|
5734
|
-
// INITIATE HELLO SEQUENCE
|
|
6211
|
+
// INITIATE SERVER CELL HELLO SEQUENCE
|
|
5735
6212
|
self.initiateHelloSequence(socketToServerClientInstance);
|
|
5736
6213
|
|
|
5737
6214
|
// We are connected:
|
|
@@ -5755,47 +6232,68 @@ class AORTACServerCell{
|
|
|
5755
6232
|
|
|
5756
6233
|
}
|
|
5757
6234
|
|
|
5758
|
-
|
|
5759
6235
|
/*private*/handleCommonListeners(){
|
|
5760
|
-
|
|
5761
6236
|
// HANDLE CELL IS READY SEQUENCE
|
|
5762
6237
|
this.handleCellIsReadySequence();
|
|
5763
|
-
|
|
5764
6238
|
// HANDLE PARTITION SEQUENCE
|
|
5765
6239
|
this.handlePartitionSequence();
|
|
5766
6240
|
|
|
5767
|
-
|
|
6241
|
+
|
|
6242
|
+
// ******************************************
|
|
6243
|
+
// CLIENT
|
|
6244
|
+
// HANDLE CLIENT MODEL INITIAL POPULATION SEQUENCE
|
|
6245
|
+
this.handleGetServersForZoneRequest();
|
|
6246
|
+
|
|
6247
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
6248
|
+
this.handleReconnectedClientHelloSequence();
|
|
5768
6249
|
|
|
6250
|
+
}
|
|
6251
|
+
|
|
5769
6252
|
|
|
5770
6253
|
/*private*/executeListeners(outletName, channelName, message, clientSocket){
|
|
5771
6254
|
const onReceiveMessageListeners=this.onReceiveMessageListeners[outletName][channelName];
|
|
5772
6255
|
if(!onReceiveMessageListeners) return;
|
|
5773
6256
|
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
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");
|
|
5788
6280
|
}
|
|
5789
6281
|
|
|
5790
6282
|
|
|
5791
6283
|
/*private*/sendMessageToBlob(channelName, message,
|
|
5792
|
-
broadcastConfig={
|
|
6284
|
+
broadcastConfig={
|
|
6285
|
+
isOriginatingCell:false,
|
|
6286
|
+
destinationCellsOrigins:null,
|
|
6287
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
6288
|
+
isRequest:false,
|
|
6289
|
+
contactAllQuorumBlob:false
|
|
6290
|
+
},
|
|
6291
|
+
overridingMessageType=null){
|
|
5793
6292
|
|
|
5794
|
-
const self=this;
|
|
5795
|
-
|
|
5796
6293
|
if(broadcastConfig){
|
|
5797
6294
|
if(broadcastConfig.isOriginatingCell){
|
|
5798
6295
|
message.originatingCellOrigin=this.selfOrigin;
|
|
6296
|
+
message.originatingPartitionId=this.partitionId;
|
|
5799
6297
|
}
|
|
5800
6298
|
if(broadcastConfig.isRequest){
|
|
5801
6299
|
message.isRequest=true;
|
|
@@ -5808,10 +6306,36 @@ class AORTACServerCell{
|
|
|
5808
6306
|
if(!message.visitedCells)
|
|
5809
6307
|
message.visitedCells=[];
|
|
5810
6308
|
else if(contains(message.visitedCells,this.selfOrigin))
|
|
5811
|
-
return;
|
|
6309
|
+
return null;
|
|
5812
6310
|
message.visitedCells.push(this.selfOrigin);
|
|
5813
6311
|
|
|
5814
|
-
|
|
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){
|
|
5815
6339
|
foreach(this.incomingCells,(incomingCell)=>{
|
|
5816
6340
|
// As a server, we send (forward) the message to the currently iterated upon client that is connected to us :
|
|
5817
6341
|
incomingCell.server.send(channelName, message, null, incomingCell.clientSocket);
|
|
@@ -5819,7 +6343,6 @@ class AORTACServerCell{
|
|
|
5819
6343
|
(incomingCell.connected && !contains(message.visitedCells, incomingCellOrigin))
|
|
5820
6344
|
);
|
|
5821
6345
|
}
|
|
5822
|
-
|
|
5823
6346
|
foreach(this.outcomingCells,(outcomingCell)=>{
|
|
5824
6347
|
// As a client, we send (forward) the message to the currently iterated upon server we are connected to :
|
|
5825
6348
|
outcomingCell.socketToServerClientInstance.send(channelName, message);
|
|
@@ -5827,49 +6350,84 @@ class AORTACServerCell{
|
|
|
5827
6350
|
(outcomingCell.connected && !contains(message.visitedCells, outcomingCellOrigin))
|
|
5828
6351
|
);
|
|
5829
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
|
+
}
|
|
5830
6369
|
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
// When we have received the message :
|
|
5852
|
-
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){
|
|
5853
6390
|
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
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
|
+
}
|
|
5859
6399
|
}
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
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
|
+
};
|
|
5867
6417
|
|
|
6418
|
+
|
|
6419
|
+
|
|
6420
|
+
return blobResponseListener;
|
|
5868
6421
|
}
|
|
6422
|
+
|
|
6423
|
+
|
|
5869
6424
|
|
|
6425
|
+
// ===================================================================================================
|
|
6426
|
+
// SERVER CELLS
|
|
6427
|
+
// ===================================================================================================
|
|
5870
6428
|
|
|
5871
6429
|
// ==========================================================
|
|
5872
|
-
// HELLO SEQUENCE
|
|
6430
|
+
// SERVER CELL HELLO SEQUENCE
|
|
5873
6431
|
|
|
5874
6432
|
/*private*/initiateHelloSequence(socketToServerClientInstance){
|
|
5875
6433
|
|
|
@@ -5882,28 +6440,30 @@ class AORTACServerCell{
|
|
|
5882
6440
|
}
|
|
5883
6441
|
|
|
5884
6442
|
|
|
5885
|
-
/*private*/
|
|
6443
|
+
/*private*/handleServerCellHelloSequence(){
|
|
5886
6444
|
|
|
5887
6445
|
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
6446
|
+
this.onReceiveMessageListeners["both"]["protocol"]["helloRequest"]={
|
|
6447
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
5891
6448
|
|
|
5892
|
-
const cellOriginToCheck=
|
|
6449
|
+
const cellOriginToCheck=messageParam.originatingCellOrigin;
|
|
5893
6450
|
|
|
5894
6451
|
// TRACE
|
|
5895
|
-
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...`);
|
|
5896
6453
|
|
|
5897
6454
|
|
|
5898
6455
|
selfParam.incomingCells[cellOriginToCheck]={
|
|
5899
6456
|
connected:true,
|
|
5900
|
-
server:server,
|
|
6457
|
+
server:serverWrapper.server,
|
|
5901
6458
|
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
5902
6459
|
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
5903
6460
|
clientSocket:clientSocket,
|
|
5904
6461
|
};
|
|
5905
6462
|
|
|
5906
|
-
|
|
6463
|
+
|
|
6464
|
+
// NO SERVER CELL HELLO CONFIRMATION SENT BACK TO THE OTHER SERVER CELL (to save time)
|
|
6465
|
+
|
|
6466
|
+
},
|
|
5907
6467
|
listenerMessageType:"helloRequest"
|
|
5908
6468
|
};
|
|
5909
6469
|
|
|
@@ -5928,18 +6488,14 @@ class AORTACServerCell{
|
|
|
5928
6488
|
}
|
|
5929
6489
|
|
|
5930
6490
|
/*private*/handleCellIsReadySequence(){
|
|
5931
|
-
getOrCreateEmptyAttribute(
|
|
5932
|
-
this.onReceiveMessageListeners["both"]["protocol"],"cellIsReady")["forRequestsIssuedByOthers"]={
|
|
5933
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
5934
6491
|
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
6492
|
+
this.onReceiveMessageListeners["both"]["protocol"]["cellIsReady"]={
|
|
6493
|
+
|
|
6494
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
6495
|
+
|
|
6496
|
+
selfParam.cellsOverview[messageParam.originatingCellOrigin]={ready:true,startTime:messageParam.startTime};
|
|
5938
6497
|
|
|
5939
|
-
|
|
5940
|
-
// lognow("INFO : Updated cells overview for this cell ("+selfParam.selfOrigin+") :",selfParam.cellsOverview);
|
|
5941
|
-
// lognow("DEBUG : selfParam.outcomingCells :",Object.keys(selfParam.outcomingCells));
|
|
5942
|
-
// lognow("DEBUG : selfParam.incomingCells :",Object.keys(selfParam.incomingCells));
|
|
6498
|
+
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
5943
6499
|
|
|
5944
6500
|
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
5945
6501
|
selfParam.initiateQuorumIsReachedSequenceIfNecessary();
|
|
@@ -5965,11 +6521,11 @@ class AORTACServerCell{
|
|
|
5965
6521
|
const quorumCells=copy(this.cellsOverview);
|
|
5966
6522
|
|
|
5967
6523
|
// TRACE
|
|
5968
|
-
lognow("INFO : Quorum is reached and this is the latest started cell. quorumCells
|
|
5969
|
-
|
|
5970
|
-
this.
|
|
5971
|
-
|
|
5972
|
-
|
|
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);
|
|
5973
6529
|
|
|
5974
6530
|
}
|
|
5975
6531
|
|
|
@@ -5980,249 +6536,694 @@ class AORTACServerCell{
|
|
|
5980
6536
|
// ==========================================================
|
|
5981
6537
|
// MODEL MANAGEMENT
|
|
5982
6538
|
|
|
5983
|
-
/*private*/
|
|
5984
|
-
|
|
5985
|
-
const self=this;
|
|
5986
|
-
|
|
5987
|
-
|
|
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});
|
|
5988
6771
|
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
controller.initializeModelForAORTACNode(model);
|
|
5996
|
-
|
|
5997
|
-
// This must return the asked number of partitions, indexed by partition id :
|
|
5998
|
-
// CAUTION : The partition function MUST return ALL the objects in a partition, WHATEVER THEIR NESTING LEVEL !!!
|
|
5999
|
-
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);
|
|
6000
6778
|
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
// We send the models objects to the required cells :
|
|
6006
|
-
let i=1;
|
|
6007
|
-
foreach(quorumCells, (quorumCell,quorumCellOrigin)=>{
|
|
6779
|
+
selfParam2.lobuleZone.clear(clientIdLocal);
|
|
6780
|
+
|
|
6781
|
+
});
|
|
6782
|
+
|
|
6008
6783
|
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
const message=JSON.decycle({type:"modelPartition",partition:modelPartition});
|
|
6013
|
-
|
|
6014
|
-
// TRACE
|
|
6015
|
-
lognow(`INFO : Cell ${self.selfOrigin} is sending a partition to cell ${quorumCellOrigin}...`,message);
|
|
6016
|
-
|
|
6017
|
-
self.sendMessageToBlob("protocol", message,
|
|
6018
|
-
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin], includeIncomingConnectionInTransmission:false, isRequest:false});
|
|
6784
|
+
},
|
|
6785
|
+
listenerMessageType:"clientHello"
|
|
6786
|
+
};
|
|
6019
6787
|
|
|
6020
|
-
i++;
|
|
6021
|
-
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
// Each quorum member cell is responsible for a model partition
|
|
6025
|
-
// Then the sattelite cells will be handling the duplication
|
|
6026
|
-
|
|
6027
6788
|
}
|
|
6028
|
-
|
|
6029
6789
|
|
|
6030
|
-
// HANDLE PARTITION SEQUENCE
|
|
6031
6790
|
|
|
6032
|
-
|
|
6791
|
+
// This is what other cells do when they receive a request for «getServersForZone» from a cell in the blob
|
|
6792
|
+
/*private*/handleGetServersForZoneRequest(){
|
|
6033
6793
|
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
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
|
+
|
|
6038
6820
|
|
|
6039
|
-
|
|
6040
|
-
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
6821
|
+
const message={type:"getServersForZone.response", objectsCount:allObjectsCorrespondingToZoneInServerCell.length, answeringServerOrigin:selfParam.selfOrigin};
|
|
6041
6822
|
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
const message=JSON.recycle(messageParam);
|
|
6045
|
-
|
|
6046
|
-
selfParam.doOnModelPartitionReception(message.partition);
|
|
6047
|
-
|
|
6048
|
-
}
|
|
6823
|
+
// TRACE
|
|
6824
|
+
lognow(`INFO : Server cell ${selfParam.selfOrigin} is sending its origin to server cell ${messageParam.originatingCellOrigin}...: message:`, message);
|
|
6049
6825
|
|
|
6050
|
-
|
|
6051
|
-
listenerMessageType:"
|
|
6826
|
+
selfParam.replyToBlobRequest("protocol", messageParam, message);
|
|
6827
|
+
},listenerMessageType:"getServersForZone"
|
|
6052
6828
|
};
|
|
6053
6829
|
|
|
6054
6830
|
}
|
|
6055
6831
|
|
|
6056
6832
|
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
lognow(`INFO : Incoming partition for cell ${this.selfOrigin}. Updating local model...`,partition);
|
|
6061
|
-
lognow(`>>>>`,stringifyObject(JSON.decycle(partition.objects),1));
|
|
6833
|
+
// ----
|
|
6834
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
6835
|
+
handleReconnectedClientHelloSequence(){
|
|
6062
6836
|
|
|
6837
|
+
// Here we only want to collect the servers on which to reconnect to later :
|
|
6063
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
|
+
|
|
6064
6875
|
|
|
6065
6876
|
}
|
|
6066
6877
|
|
|
6067
6878
|
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
const self=this;
|
|
6071
|
-
|
|
6072
|
-
if(!currentObject){
|
|
6073
|
-
foreach(linkedObjects, obj=>{
|
|
6074
|
-
self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
6075
|
-
});
|
|
6076
|
-
}else{
|
|
6077
|
-
self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
6078
|
-
}
|
|
6079
|
-
|
|
6080
|
-
return linkedObjects;
|
|
6081
|
-
}
|
|
6879
|
+
// ==========================================================
|
|
6082
6880
|
|
|
6083
|
-
|
|
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
|
+
// }
|
|
6084
6917
|
|
|
6085
|
-
const self=this;
|
|
6086
|
-
foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
6087
|
-
// We only remove links to the objects not in the partition
|
|
6088
|
-
if(isClassObject(attr)){
|
|
6089
|
-
let aortacId=attr.aortacId;
|
|
6090
|
-
if(!aortacId){
|
|
6091
|
-
aortacId=getUUID();
|
|
6092
|
-
attr.aortacId=aortacId;
|
|
6093
|
-
}
|
|
6094
|
-
if(!contains(linkedObjects,attr)){
|
|
6095
|
-
currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
6096
|
-
// CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
6097
|
-
// WHATEVER THEIR NESTING LEVEL !!!
|
|
6098
|
-
}
|
|
6099
|
-
}else{
|
|
6100
|
-
// However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
6101
|
-
self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
6102
|
-
}
|
|
6103
|
-
},obj=>(isObject(obj) || isArray(obj)));
|
|
6104
6918
|
|
|
6105
|
-
}
|
|
6106
6919
|
|
|
6107
6920
|
|
|
6108
|
-
//
|
|
6109
|
-
|
|
6110
|
-
/*private*/
|
|
6921
|
+
// **********************************************************************
|
|
6922
|
+
|
|
6923
|
+
/*private*/partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell=[]){
|
|
6111
6924
|
|
|
6112
|
-
|
|
6113
|
-
lognow("DEBUG : collectDependencies()...",inputObjects);
|
|
6925
|
+
const rootContainerLocal=this.model.getRootContainer();
|
|
6114
6926
|
|
|
6927
|
+
const rootContainer=this.model.populateRootContainerWithObjectsSubset(rootContainerLocal, allObjectsCorrespondingToZoneInServerCell, true);
|
|
6928
|
+
rootContainer.isRootContainer=true;
|
|
6115
6929
|
|
|
6930
|
+
return rootContainer;
|
|
6116
6931
|
}
|
|
6117
|
-
|
|
6118
|
-
/*private*/repercutChangesIfNeeded(inputObjects){
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
////
|
|
6122
|
-
lognow("DEBUG : repercutChangesIfNeeded()...",inputObjects);
|
|
6123
6932
|
|
|
6124
|
-
|
|
6125
|
-
}
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
6933
|
}
|
|
6129
6934
|
|
|
6130
6935
|
|
|
6131
6936
|
|
|
6132
|
-
// ******************************************************************
|
|
6133
6937
|
|
|
6134
|
-
// Public static hydration method :
|
|
6135
|
-
window.ao=(incompleteModelObjectToDecorate)=>{
|
|
6136
|
-
|
|
6137
|
-
const localServerCell=window.aortacCServerNodeInstance;
|
|
6138
|
-
if(!localServerCell){
|
|
6139
|
-
// TRACE
|
|
6140
|
-
lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
6141
|
-
return incompleteModelObjectToDecorate;
|
|
6142
|
-
}
|
|
6143
|
-
|
|
6144
|
-
const liveModelObjects=localServerCell.liveModelObjects;
|
|
6145
|
-
|
|
6146
|
-
// First we clone the object, for its attriutes values information :
|
|
6147
|
-
const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
6148
|
-
|
|
6149
|
-
// Then we replace all its methods :
|
|
6150
|
-
foreach(clonedObject, (method, methodName)=>{
|
|
6151
|
-
clonedObject[methodName]=new Proxy(method,
|
|
6152
|
-
{
|
|
6153
|
-
apply: function(methodToEnhance, thisArg, argumentsList) {
|
|
6154
6938
|
|
|
6155
|
-
const inputObjects=[thisArg];
|
|
6156
|
-
inputObjects.push(...argumentsList);
|
|
6157
|
-
localServerCell.collectDependencies(inputObjects);
|
|
6158
|
-
|
|
6159
|
-
// // --- Treatment BEFORE function execution ---
|
|
6160
|
-
// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
6161
|
-
|
|
6162
|
-
// Call the original function (target) with its intended 'this' context (thisArg)
|
|
6163
|
-
// and arguments (argumentsList) using Reflect.apply
|
|
6164
|
-
const result = Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
6165
|
-
|
|
6166
|
-
// // --- Treatment AFTER function execution ---
|
|
6167
|
-
// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
6168
|
-
|
|
6169
|
-
inputObjects.push(result);
|
|
6170
|
-
localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
6171
6939
|
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
},attribute=>isFunction(attribute));
|
|
6940
|
+
|
|
6941
|
+
|
|
6942
|
+
|
|
6943
|
+
class ServerCellLobule{
|
|
6177
6944
|
|
|
6178
|
-
|
|
6179
|
-
|
|
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
|
+
}
|
|
6180
7008
|
|
|
6181
7009
|
|
|
6182
7010
|
|
|
6183
|
-
window.
|
|
7011
|
+
window.getAORTACServerCell=function(quorumNumber=1, selfOrigin="ws://127.0.0.1:40000", outcomingCellsOrigins=[], model, controller){
|
|
6184
7012
|
//return new AORTACServerNode("node_"+getUUID("short"), selfOrigin, outcomingCellsOrigins, model, controller);
|
|
6185
|
-
if(window.
|
|
7013
|
+
if(window.aortacCServerCellInstance){
|
|
6186
7014
|
// TRACE
|
|
6187
|
-
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.");
|
|
6188
7016
|
}
|
|
6189
|
-
window.
|
|
6190
|
-
return window.
|
|
7017
|
+
window.aortacCServerCellInstance=new AORTACServerCell(quorumNumber, selfOrigin, outcomingCellsOrigins, model, controller);
|
|
7018
|
+
return window.aortacCServerCellInstance;
|
|
6191
7019
|
}
|
|
6192
7020
|
|
|
6193
7021
|
|
|
6194
7022
|
|
|
6195
7023
|
|
|
7024
|
+
// ===============================================================================================================================
|
|
7025
|
+
// ===============================================================================================================================
|
|
7026
|
+
// UTILITY METHODS
|
|
6196
7027
|
|
|
6197
7028
|
|
|
7029
|
+
// USAGE EXAMPLE :
|
|
6198
7030
|
|
|
6199
|
-
// ==================================================================================================================
|
|
6200
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
|
|
6201
7058
|
|
|
6202
|
-
// AORTAC CLIENT
|
|
6203
7059
|
|
|
7060
|
+
window.proxyClass=function(classToProxy,
|
|
7061
|
+
doOnAccess=(className, propertyName, parameter)=>{lognow(`Accessing ${className}.${propertyName}:`,parameter);}){
|
|
6204
7062
|
|
|
7063
|
+
const className=classToProxy.name;
|
|
6205
7064
|
|
|
6206
|
-
|
|
7065
|
+
// Build the Proxy handler
|
|
7066
|
+
const proxyHandler={
|
|
6207
7067
|
|
|
6208
|
-
//
|
|
7068
|
+
// Intercepts: obj.property AND obj.method()
|
|
7069
|
+
get(concernedObject, property, thisArg){
|
|
7070
|
+
const value=Reflect.get(concernedObject, property, thisArg);
|
|
6209
7071
|
|
|
6210
|
-
|
|
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=()=>{
|
|
6211
7155
|
|
|
6212
|
-
|
|
7156
|
+
const aortacConfig=GAME_CONFIG.aortac;
|
|
7157
|
+
if(nothing(aortacConfig)){
|
|
7158
|
+
// TRACE
|
|
7159
|
+
lognow("WARN : No AORTAC config, Aborting..");
|
|
7160
|
+
return null;
|
|
7161
|
+
}
|
|
6213
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+"».");
|
|
6214
7171
|
}
|
|
6215
7172
|
|
|
6216
|
-
|
|
7173
|
+
return currentLevelPrototypeName;
|
|
7174
|
+
};
|
|
6217
7175
|
|
|
6218
7176
|
|
|
6219
7177
|
|
|
6220
7178
|
|
|
6221
|
-
window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
6222
|
-
//return new AORTACClient("client_"+getUUID(), serverNodeOrigin, model, view, isNodeContext);
|
|
6223
|
-
return new AORTACClientCell(serverNodeOrigin, model, view, isNodeContext);
|
|
6224
|
-
}
|
|
6225
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
|
+
//};
|
|
6226
7227
|
|
|
6227
7228
|
|
|
6228
7229
|
|
|
@@ -6938,7 +7939,7 @@ WebsocketImplementation={
|
|
|
6938
7939
|
},
|
|
6939
7940
|
|
|
6940
7941
|
|
|
6941
|
-
/*
|
|
7942
|
+
/*public*//*static*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
|
|
6942
7943
|
|
|
6943
7944
|
const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
|
|
6944
7945
|
|
|
@@ -7135,12 +8136,11 @@ WebsocketImplementation={
|
|
|
7135
8136
|
|
|
7136
8137
|
|
|
7137
8138
|
|
|
7138
|
-
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){
|
|
7139
8140
|
|
|
7140
8141
|
const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
|
|
7141
8142
|
|
|
7142
8143
|
|
|
7143
|
-
|
|
7144
8144
|
if(typeof(https)==="undefined"){
|
|
7145
8145
|
// TRACE
|
|
7146
8146
|
console.log("«https» SERVER library not called yet, calling it now.");
|
|
@@ -7232,8 +8232,6 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7232
8232
|
|
|
7233
8233
|
const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
|
|
7234
8234
|
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
8235
|
let listenableServer;
|
|
7238
8236
|
if(sslOptions){
|
|
7239
8237
|
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
@@ -7254,7 +8252,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7254
8252
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
7255
8253
|
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
7256
8254
|
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
7257
|
-
});
|
|
8255
|
+
}, doOnConnectionLost);
|
|
7258
8256
|
|
|
7259
8257
|
|
|
7260
8258
|
server.onFinalize((serverParam)=>{
|
|
@@ -7269,15 +8267,13 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
7269
8267
|
// TRACE
|
|
7270
8268
|
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
7271
8269
|
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
8270
|
|
|
7275
8271
|
|
|
7276
8272
|
return server;
|
|
7277
8273
|
}
|
|
7278
8274
|
|
|
7279
8275
|
|
|
7280
|
-
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
|
|
8276
|
+
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, doOnConnectionLost=null,
|
|
7281
8277
|
/*OPTIONAL*/portParam,
|
|
7282
8278
|
/*OPTIONAL*/certPathParam,
|
|
7283
8279
|
/*OPTIONAL*/keyPathParam){
|
|
@@ -7330,13 +8326,11 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7330
8326
|
|
|
7331
8327
|
|
|
7332
8328
|
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
8329
|
const aotraNodeServer={config:serverConfig};
|
|
7336
8330
|
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
7337
8331
|
|
|
7338
8332
|
if(isHashAsked){
|
|
7339
|
-
// We
|
|
8333
|
+
// We instantiate a temporary persister just to read the key hash file:
|
|
7340
8334
|
const persister=getPersister("./");
|
|
7341
8335
|
let persisterIdSplits=persisterId.split("@");
|
|
7342
8336
|
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
@@ -7419,9 +8413,9 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7419
8413
|
}
|
|
7420
8414
|
}
|
|
7421
8415
|
|
|
7422
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
8416
|
+
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, doOnConnectionLost, sslOptions);
|
|
8417
|
+
|
|
7423
8418
|
|
|
7424
|
-
|
|
7425
8419
|
return aotraNodeServer;
|
|
7426
8420
|
};
|
|
7427
8421
|
|
|
@@ -7484,7 +8478,10 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
7484
8478
|
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
7485
8479
|
//
|
|
7486
8480
|
//
|
|
7487
|
-
// },
|
|
8481
|
+
// },
|
|
8482
|
+
// // On client connection lost :
|
|
8483
|
+
// (clientId)=>{lognow(`INFO : Connection to client id «${clientId}» was lost.`);},
|
|
8484
|
+
// portParam,certPathParam,keyPathParam);
|
|
7488
8485
|
//
|
|
7489
8486
|
//
|
|
7490
8487
|
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
@@ -7952,8 +8949,8 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
7952
8949
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
7953
8950
|
socketToServerClientInstance.onConnectionToServer(()=>{
|
|
7954
8951
|
if(doOnServerConnection){
|
|
7955
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance]);
|
|
7956
|
-
else doOnServerConnection(socketToServerClientInstance);
|
|
8952
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance, aotraClient]);
|
|
8953
|
+
else doOnServerConnection(socketToServerClientInstance, aotraClient);
|
|
7957
8954
|
}
|
|
7958
8955
|
|
|
7959
8956
|
|
|
@@ -8050,8 +9047,9 @@ class ClientReceptionEntryPoint{
|
|
|
8050
9047
|
|
|
8051
9048
|
|
|
8052
9049
|
// We check if the message matches the required message type :
|
|
8053
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
8054
|
-
&&
|
|
9050
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
9051
|
+
&& dataWrapped.data
|
|
9052
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
8055
9053
|
return;
|
|
8056
9054
|
}
|
|
8057
9055
|
|
|
@@ -8075,8 +9073,12 @@ class ClientReceptionEntryPoint{
|
|
|
8075
9073
|
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
8076
9074
|
remove(clientReceptionEntryPoints, this);
|
|
8077
9075
|
|
|
8078
|
-
if(this.doOnIncomingMessage)
|
|
8079
|
-
|
|
9076
|
+
if(this.doOnIncomingMessage){
|
|
9077
|
+
|
|
9078
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
9079
|
+
|
|
9080
|
+
this.doOnIncomingMessage(dataRestored, clientSocket);
|
|
9081
|
+
}
|
|
8080
9082
|
|
|
8081
9083
|
}
|
|
8082
9084
|
|
|
@@ -8119,7 +9121,9 @@ class ClientInstance{
|
|
|
8119
9121
|
// I-
|
|
8120
9122
|
//
|
|
8121
9123
|
self.receive(channelNameForResponse, doOnIncomingMessageForResponse,
|
|
8122
|
-
|
|
9124
|
+
{messageId:messageId, destroyListenerAfterReceiving:true},
|
|
9125
|
+
clientsRoomsTag, listenerId,
|
|
9126
|
+
);
|
|
8123
9127
|
//
|
|
8124
9128
|
|
|
8125
9129
|
return resultPromise;
|
|
@@ -8134,7 +9138,7 @@ class ClientInstance{
|
|
|
8134
9138
|
}
|
|
8135
9139
|
|
|
8136
9140
|
// II-
|
|
8137
|
-
receive(channelNameParam, doOnIncomingMessage,
|
|
9141
|
+
receive(channelNameParam, doOnIncomingMessage, listenerConfig={destroyListenerAfterReceiving:false, listenerMessageType:null}, clientsRoomsTag=null, entryPointId=null){
|
|
8138
9142
|
const self=this;
|
|
8139
9143
|
|
|
8140
9144
|
// // DBG
|
|
@@ -8174,8 +9178,11 @@ class ClientInstance{
|
|
|
8174
9178
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8175
9179
|
|
|
8176
9180
|
// Channel information is stored in exchanged data :
|
|
8177
|
-
let dataWrapped={channelName:channelNameParam, data:data};
|
|
8178
|
-
|
|
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!)
|
|
8179
9186
|
dataWrapped=stringifyObject(dataWrapped);
|
|
8180
9187
|
|
|
8181
9188
|
// TODO : FIXME : Use one single interface !
|
|
@@ -8307,8 +9314,9 @@ class ServerReceptionEntryPoint{
|
|
|
8307
9314
|
if(!isClientInRoom) return;
|
|
8308
9315
|
|
|
8309
9316
|
// We check if the message matches the required message type :
|
|
8310
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
8311
|
-
|
|
9317
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
9318
|
+
&& dataWrapped.data
|
|
9319
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
8312
9320
|
return;
|
|
8313
9321
|
}
|
|
8314
9322
|
|
|
@@ -8316,7 +9324,11 @@ class ServerReceptionEntryPoint{
|
|
|
8316
9324
|
// // DBG
|
|
8317
9325
|
// lognow("(SERVER) this.doOnIncomingMessage:");
|
|
8318
9326
|
|
|
8319
|
-
|
|
9327
|
+
const dataLocal=dataWrapped.data;
|
|
9328
|
+
|
|
9329
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
9330
|
+
|
|
9331
|
+
this.doOnIncomingMessage(dataRestored, clientSocketParam);
|
|
8320
9332
|
}
|
|
8321
9333
|
|
|
8322
9334
|
}
|
|
@@ -8364,13 +9376,10 @@ class NodeServerInstance{
|
|
|
8364
9376
|
});
|
|
8365
9377
|
}
|
|
8366
9378
|
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
9379
|
// TODO : DEVELOP...
|
|
8370
9380
|
//sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
8371
9381
|
//}
|
|
8372
9382
|
|
|
8373
|
-
|
|
8374
9383
|
receive(channelNameParam, doOnIncomingMessage, listenerConfig=null, clientsRoomsTag=null){
|
|
8375
9384
|
|
|
8376
9385
|
// DBG
|
|
@@ -8418,14 +9427,16 @@ class NodeServerInstance{
|
|
|
8418
9427
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8419
9428
|
|
|
8420
9429
|
// Channel information is stored in exchanged data :
|
|
8421
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8422
|
-
|
|
8423
|
-
|
|
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!)
|
|
8424
9435
|
dataWrapped=stringifyObject(dataWrapped);
|
|
8425
9436
|
|
|
8426
9437
|
// TODO : FIXME : Use one single interface !
|
|
8427
9438
|
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
8428
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
9439
|
+
else clientSocket.emit(channelName, dataWrapped);
|
|
8429
9440
|
|
|
8430
9441
|
});
|
|
8431
9442
|
|
|
@@ -8445,12 +9456,12 @@ class NodeServerInstance{
|
|
|
8445
9456
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
8446
9457
|
|
|
8447
9458
|
// Channel information is stored in exchanged data :
|
|
8448
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
8449
|
-
dataWrapped=stringifyObject(dataWrapped);
|
|
9459
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
8450
9460
|
|
|
8451
|
-
|
|
8452
|
-
//
|
|
8453
|
-
|
|
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);
|
|
8454
9465
|
|
|
8455
9466
|
|
|
8456
9467
|
// TODO : FIXME : Use one single interface !
|
|
@@ -8463,8 +9474,8 @@ class NodeServerInstance{
|
|
|
8463
9474
|
return this;
|
|
8464
9475
|
}
|
|
8465
9476
|
|
|
8466
|
-
|
|
8467
|
-
onConnectionToClient(doOnConnection){
|
|
9477
|
+
|
|
9478
|
+
onConnectionToClient(doOnConnection, doOnConnectionLost=null){
|
|
8468
9479
|
const self=this;
|
|
8469
9480
|
|
|
8470
9481
|
|
|
@@ -8473,7 +9484,6 @@ class NodeServerInstance{
|
|
|
8473
9484
|
|
|
8474
9485
|
// DBG
|
|
8475
9486
|
console.log("SERVER : ON CONNECTION !");
|
|
8476
|
-
|
|
8477
9487
|
|
|
8478
9488
|
const clientId="autogeneratedid_"+getUUID();
|
|
8479
9489
|
|
|
@@ -8524,6 +9534,9 @@ class NodeServerInstance{
|
|
|
8524
9534
|
|
|
8525
9535
|
|
|
8526
9536
|
clearInterval(clientSocket.stateCheckInterval);
|
|
9537
|
+
|
|
9538
|
+
if(doOnConnectionLost) doOnConnectionLost(clientSocket.clientId);
|
|
9539
|
+
|
|
8527
9540
|
return;
|
|
8528
9541
|
}
|
|
8529
9542
|
|
|
@@ -8582,7 +9595,71 @@ class NodeServerInstance{
|
|
|
8582
9595
|
|
|
8583
9596
|
|
|
8584
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)) );
|
|
8585
9660
|
|
|
9661
|
+
return obj;
|
|
9662
|
+
};
|
|
8586
9663
|
|
|
8587
9664
|
|
|
8588
9665
|
|