aotrautils 0.0.1841 → 0.0.1847
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aotrautils/aotrautils.build.js +1542 -713
- aotrautils/package.json +1 -1
aotrautils/aotrautils.build.js
CHANGED
|
@@ -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:05:39)»*/
|
|
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);
|
|
5232
|
+
return this;
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5235
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5236
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5191
5237
|
return this;
|
|
5192
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 CLIENT library associated with aotra version : «1_29072022-2359 (
|
|
5461
|
+
/*utils CLIENT library associated with aotra version : «1_29072022-2359 (02/06/2026-01:05:39)»*/
|
|
5406
5462
|
/*-----------------------------------------------------------------------------*/
|
|
5407
5463
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
5408
5464
|
*
|
|
@@ -6117,7 +6173,7 @@ function getTextWordsExtract(textStrParam, wordsNumber,/* OPTIONAL */wordPositio
|
|
|
6117
6173
|
|
|
6118
6174
|
//==================== CLIENT Geometry ====================
|
|
6119
6175
|
|
|
6120
|
-
Math.isInScreen=function(coords,size=null,center=null,totalWidth,totalHeight,camera=null){
|
|
6176
|
+
Math.isInScreen=function(coords, size=null, center=null, totalWidth, totalHeight, camera=null){
|
|
6121
6177
|
|
|
6122
6178
|
if(center==null) center={x:"center",y:"center"};
|
|
6123
6179
|
|
|
@@ -6141,8 +6197,6 @@ Math.isInScreen=function(coords,size=null,center=null,totalWidth,totalHeight,cam
|
|
|
6141
6197
|
}
|
|
6142
6198
|
|
|
6143
6199
|
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
6200
|
let isInScreenX=false;
|
|
6147
6201
|
{
|
|
6148
6202
|
let min;
|
|
@@ -8321,7 +8375,7 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
8321
8375
|
|
|
8322
8376
|
// This temporary, off-screen video is always needed to get the image data for further use :
|
|
8323
8377
|
videoTag=document.createElement("video");
|
|
8324
|
-
videoTag.id="tmpVideoElement";
|
|
8378
|
+
videoTag.id="tmpVideoElement"+getUUID("short");
|
|
8325
8379
|
videoTag.autoplay=true;
|
|
8326
8380
|
videoTag.width=canvasTag.width;
|
|
8327
8381
|
videoTag.height=canvasTag.height;
|
|
@@ -8661,12 +8715,6 @@ drawVideoImage=function(canvas,videoImage,
|
|
|
8661
8715
|
var x=nonull(xParam,0);
|
|
8662
8716
|
var y=nonull(yParam,0);
|
|
8663
8717
|
|
|
8664
|
-
// // DOES NOT WORK WELL :
|
|
8665
|
-
// if(Object.getPrototypeOf(videoImage).constructor.name==="VideoFrame"){
|
|
8666
|
-
// ctx.drawImage(videoImage, x, y, newWidthParam, newHeightParam);
|
|
8667
|
-
// return;
|
|
8668
|
-
// }
|
|
8669
|
-
|
|
8670
8718
|
var oldWidth=videoImage.width;
|
|
8671
8719
|
var oldHeight=videoImage.height;
|
|
8672
8720
|
|
|
@@ -9500,14 +9548,18 @@ class SpriteMonoThreaded{
|
|
|
9500
9548
|
// Time measurement :
|
|
9501
9549
|
this.time=getNow();
|
|
9502
9550
|
this.refreshMillis=refreshMillis;
|
|
9503
|
-
this.durationTimeFactorHolder={durationTimeFactor:1};
|
|
9504
9551
|
|
|
9552
|
+
this.managedTimeFactor=1;
|
|
9505
9553
|
// Two-states : started, stopped.
|
|
9506
|
-
|
|
9554
|
+
}
|
|
9555
|
+
|
|
9556
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
9557
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
9558
|
+
return this;
|
|
9507
9559
|
}
|
|
9508
9560
|
|
|
9509
|
-
|
|
9510
|
-
this.
|
|
9561
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
9562
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
9511
9563
|
return this;
|
|
9512
9564
|
}
|
|
9513
9565
|
|
|
@@ -9577,7 +9629,7 @@ class SpriteMonoThreaded{
|
|
|
9577
9629
|
}
|
|
9578
9630
|
|
|
9579
9631
|
hasDelayPassed(){
|
|
9580
|
-
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.
|
|
9632
|
+
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor));
|
|
9581
9633
|
}
|
|
9582
9634
|
|
|
9583
9635
|
}
|
|
@@ -13228,7 +13280,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13228
13280
|
|
|
13229
13281
|
|
|
13230
13282
|
|
|
13231
|
-
/*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (
|
|
13283
|
+
/*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (02/06/2026-01:05:39)»*/
|
|
13232
13284
|
/*-----------------------------------------------------------------------------*/
|
|
13233
13285
|
|
|
13234
13286
|
|
|
@@ -13983,171 +14035,6 @@ window.getSunShadowProjectedPoint=function(originalPoint,angleDegreesParam,groun
|
|
|
13983
14035
|
}
|
|
13984
14036
|
|
|
13985
14037
|
|
|
13986
|
-
window.isInZone=function(point, zone, center={x:"center",y:"center",z:"center"}, zoneOffsets=null, invertYAxis=false, zooms=null){
|
|
13987
|
-
|
|
13988
|
-
if(!center){
|
|
13989
|
-
center={x:"center",y:"center",z:"center"}
|
|
13990
|
-
}
|
|
13991
|
-
if(invertYAxis==null){
|
|
13992
|
-
invertYAxis=false;
|
|
13993
|
-
}
|
|
13994
|
-
if(zooms){
|
|
13995
|
-
point=getZoomedPosition(point, zooms);
|
|
13996
|
-
zone=getZoomedZone(zone, zooms);
|
|
13997
|
-
}
|
|
13998
|
-
|
|
13999
|
-
let pointX=point.x;
|
|
14000
|
-
let pointY=point.y;
|
|
14001
|
-
let pointZ=point.z;
|
|
14002
|
-
|
|
14003
|
-
let zoneX=zone.x;
|
|
14004
|
-
let zoneY=zone.y;
|
|
14005
|
-
let zoneZ=zone.z;
|
|
14006
|
-
if(zoneOffsets){
|
|
14007
|
-
if(zoneX!=null) zoneX+=nonull(zoneOffsets.x,0);
|
|
14008
|
-
if(zoneY!=null) zoneY+=nonull(zoneOffsets.y,0);
|
|
14009
|
-
if(zoneZ!=null) zoneZ+=nonull(zoneOffsets.z,0);
|
|
14010
|
-
}
|
|
14011
|
-
|
|
14012
|
-
// We ignore zone parameters if they are null or 0 :
|
|
14013
|
-
let isXInZone=zone.w?false:true;
|
|
14014
|
-
let isYInZone=zone.h?false:true;
|
|
14015
|
-
let isZInZone=zone.d?false:true;
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
if(zone.w || zone.h || zone.d){
|
|
14019
|
-
|
|
14020
|
-
if(zone.w){
|
|
14021
|
-
if(pointX!=null && zoneX!=null){
|
|
14022
|
-
if(center.x==="center"){
|
|
14023
|
-
let demiSize=Math.floor(zone.w*.5);
|
|
14024
|
-
if((zoneX - demiSize) <= pointX && pointX <= (zoneX + demiSize) )
|
|
14025
|
-
isXInZone=true;
|
|
14026
|
-
}else if(center.x==="right"){
|
|
14027
|
-
if((zoneX - zone.w) <= pointX && pointX <= zoneX )
|
|
14028
|
-
isXInZone=true;
|
|
14029
|
-
}else if(center.x==="left"){
|
|
14030
|
-
if(zoneX <= pointX && pointX <= (zoneX + zone.w) )
|
|
14031
|
-
isXInZone=true;
|
|
14032
|
-
}
|
|
14033
|
-
}else{
|
|
14034
|
-
isXInZone=true;
|
|
14035
|
-
}
|
|
14036
|
-
}
|
|
14037
|
-
|
|
14038
|
-
if(zone.h){
|
|
14039
|
-
if(pointY!=null && zoneY!=null){
|
|
14040
|
-
if(center.y==="center"){
|
|
14041
|
-
let demiSize=Math.floor(zone.h*.5);
|
|
14042
|
-
if((zoneY - demiSize) <= pointY && pointY <= (zoneY + demiSize) )
|
|
14043
|
-
isYInZone=true;
|
|
14044
|
-
}else if(center.y==="top"){
|
|
14045
|
-
if(!invertYAxis){
|
|
14046
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14047
|
-
isYInZone=true;
|
|
14048
|
-
}else{
|
|
14049
|
-
if(zoneY <= pointY && pointY <= (zoneY + zone.h) )
|
|
14050
|
-
isYInZone=true;
|
|
14051
|
-
}
|
|
14052
|
-
}else if(center.y==="bottom"){
|
|
14053
|
-
if(!invertYAxis){
|
|
14054
|
-
if(zoneY <= pointY && pointY <= (zoneY + zone.h) )
|
|
14055
|
-
isYInZone=true;
|
|
14056
|
-
}else{
|
|
14057
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14058
|
-
isYInZone=true;
|
|
14059
|
-
}
|
|
14060
|
-
}
|
|
14061
|
-
}else{
|
|
14062
|
-
isYInZone=true;
|
|
14063
|
-
}
|
|
14064
|
-
}
|
|
14065
|
-
|
|
14066
|
-
if(zone.d){
|
|
14067
|
-
if(pointZ!=null && zoneZ!=null){
|
|
14068
|
-
if(center.z==="center"){
|
|
14069
|
-
let demiSize=Math.floor(zone.d*.5);
|
|
14070
|
-
if((zoneZ - demiSize) <= pointZ && pointZ <= (zoneZ + demiSize) )
|
|
14071
|
-
isZInZone=true;
|
|
14072
|
-
}else if(center.z==="front"){
|
|
14073
|
-
if((zoneZ - zone.d) <= pointZ && pointZ <= zoneZ )
|
|
14074
|
-
isZInZone=true;
|
|
14075
|
-
}else if(center.z==="back"){
|
|
14076
|
-
if(zoneZ <= pointZ && pointZ <= (zoneZ + zone.d) )
|
|
14077
|
-
isZInZone=true;
|
|
14078
|
-
}
|
|
14079
|
-
}else{
|
|
14080
|
-
isZInZone=true;
|
|
14081
|
-
}
|
|
14082
|
-
}
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
}else{
|
|
14086
|
-
|
|
14087
|
-
if(pointX!=null && zoneX!=null){
|
|
14088
|
-
if(center.x==="center"){
|
|
14089
|
-
let demiW=Math.floor(zone.w*.5);
|
|
14090
|
-
if((zoneX - demiW) <= pointX && pointX <= (zoneX + demiW))
|
|
14091
|
-
isXInZone=true;
|
|
14092
|
-
}else if(center.x==="right"){
|
|
14093
|
-
if((zoneX - zone.w) <= pointX && pointX <= zoneX )
|
|
14094
|
-
isXInZone=true;
|
|
14095
|
-
}else if(center.x==="left"){
|
|
14096
|
-
if( zoneX <= pointX && pointX <= (zoneX + zone.w ) )
|
|
14097
|
-
isXInZone=true;
|
|
14098
|
-
}
|
|
14099
|
-
}else{
|
|
14100
|
-
isXInZone=true;
|
|
14101
|
-
}
|
|
14102
|
-
|
|
14103
|
-
if(pointY!=null && zoneY!=null){
|
|
14104
|
-
if(center.y==="center"){
|
|
14105
|
-
let demiH=Math.floor(zone.h*.5);
|
|
14106
|
-
if((zoneY - demiH) <= pointY && pointY <= (zoneY + demiH))
|
|
14107
|
-
isYInZone=true;
|
|
14108
|
-
}else if(center.y==="top"){
|
|
14109
|
-
if(!invertYAxis){
|
|
14110
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14111
|
-
isYInZone=true;
|
|
14112
|
-
}else{
|
|
14113
|
-
if( zoneY <= pointY && pointY <= (zoneY + zone.h ) )
|
|
14114
|
-
isYInZone=true;
|
|
14115
|
-
}
|
|
14116
|
-
}else if(center.y==="bottom"){
|
|
14117
|
-
if(!invertYAxis){
|
|
14118
|
-
if( zoneY <= pointY && pointY <= (zoneY + zone.h ) )
|
|
14119
|
-
isYInZone=true;
|
|
14120
|
-
}else{
|
|
14121
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14122
|
-
isYInZone=true;
|
|
14123
|
-
}
|
|
14124
|
-
}
|
|
14125
|
-
}else{
|
|
14126
|
-
isYInZone=true;
|
|
14127
|
-
}
|
|
14128
|
-
|
|
14129
|
-
if(pointZ!=null && zoneZ!=null){
|
|
14130
|
-
if(center.z==="center"){
|
|
14131
|
-
let demiW=Math.floor(zone.d*.5);
|
|
14132
|
-
if((zoneZ - demiW) <= pointZ && pointZ <= (zoneZ + demiW))
|
|
14133
|
-
isZInZone=true;
|
|
14134
|
-
}else if(center.z==="front"){
|
|
14135
|
-
if((zoneZ - zone.d) <= pointZ && pointZ <= zoneZ )
|
|
14136
|
-
isZInZone=true;
|
|
14137
|
-
}else if(center.z==="back"){
|
|
14138
|
-
if( zoneZ <= pointZ && pointZ <= (zoneZ + zone.d ) )
|
|
14139
|
-
isZInZone=true;
|
|
14140
|
-
}
|
|
14141
|
-
}else{
|
|
14142
|
-
isZInZone=true;
|
|
14143
|
-
}
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
}
|
|
14147
|
-
|
|
14148
|
-
return isXInZone && isYInZone && isZInZone;
|
|
14149
|
-
}
|
|
14150
|
-
|
|
14151
14038
|
|
|
14152
14039
|
Math.getQuadrant=function(centerPoint,point){
|
|
14153
14040
|
let deltaX=point.x-centerPoint.x;
|
|
@@ -14284,85 +14171,6 @@ Math.getBidimensional8Direction=(currentPosition,destination,invertYAxis=false)=
|
|
|
14284
14171
|
|
|
14285
14172
|
|
|
14286
14173
|
|
|
14287
|
-
/*public static*/window.getRandomPositionInZone=function(positionZone, xOffset=0, yOffset=0, zOffset=0, avoidOverlap=null, size=null, randomizeAngles=false){
|
|
14288
|
-
|
|
14289
|
-
let w=nonull(positionZone.w,0);
|
|
14290
|
-
let h=nonull(positionZone.h,0);
|
|
14291
|
-
let d=nonull(positionZone.d,0);
|
|
14292
|
-
|
|
14293
|
-
let demiW=w*.5;
|
|
14294
|
-
let demiH=h*.5;
|
|
14295
|
-
let demiD=d*.5;
|
|
14296
|
-
|
|
14297
|
-
let xOrZero=nonull(positionZone.x,0);
|
|
14298
|
-
let yOrZero=nonull(positionZone.y,0);
|
|
14299
|
-
let zOrZero=nonull(positionZone.z,0);
|
|
14300
|
-
|
|
14301
|
-
// If respectively width or height is 0, then there is no need to use a randomization !
|
|
14302
|
-
let x= ( w<=0 ? xOrZero : (xOrZero + Math.getRandomInt(Math.floor(demiW), -Math.ceil(demiW)))) + xOffset;
|
|
14303
|
-
let y= ( h<=0 ? yOrZero : (yOrZero + Math.getRandomInt(Math.floor(demiH), -Math.ceil(demiH)))) + yOffset;
|
|
14304
|
-
let z= ( d<=0 ? zOrZero : (zOrZero + Math.getRandomInt(Math.floor(demiD), -Math.ceil(demiD)))) + zOffset;
|
|
14305
|
-
|
|
14306
|
-
if(avoidOverlap && size){
|
|
14307
|
-
|
|
14308
|
-
let itemW=nonull(size.w,0);
|
|
14309
|
-
let itemH=nonull(size.h,0);
|
|
14310
|
-
let itemD=nonull(size.d,0);
|
|
14311
|
-
|
|
14312
|
-
if(xOrZero/*includes case==0*/ && 0<itemW && contains(avoidOverlap,"x")) x=Math.floor(x/itemW)*itemW;
|
|
14313
|
-
if(yOrZero/*includes case==0*/ && 0<itemH && contains(avoidOverlap,"y")) y=Math.floor(y/itemH)*itemH;
|
|
14314
|
-
if(zOrZero/*includes case==0*/ && 0<itemD && contains(avoidOverlap,"z")) z=Math.floor(z/itemD)*itemD;
|
|
14315
|
-
|
|
14316
|
-
}
|
|
14317
|
-
|
|
14318
|
-
let b=positionZone.b;
|
|
14319
|
-
let g=positionZone.g;
|
|
14320
|
-
let a=positionZone.a;
|
|
14321
|
-
if(randomizeAngles){
|
|
14322
|
-
b=Math.random()*Math.PI*2;
|
|
14323
|
-
g=Math.random()*Math.PI*2;
|
|
14324
|
-
a=Math.random()*Math.PI*2;
|
|
14325
|
-
}
|
|
14326
|
-
|
|
14327
|
-
return {x:x, y:y, z:z, b:b, g:g, a:a, parallax:positionZone.parallax};
|
|
14328
|
-
}
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
14332
|
-
/*public static*/window.isZoneCollidingWithZone=function(zone1, zone2){
|
|
14333
|
-
|
|
14334
|
-
let demiWidth1=zone1.w*.5;
|
|
14335
|
-
let demiWidth2=zone2.w*.5;
|
|
14336
|
-
|
|
14337
|
-
let xMin1=zone1.x-demiWidth1;
|
|
14338
|
-
let xMax1=zone1.x+demiWidth1;
|
|
14339
|
-
let xMin2=zone2.x-demiWidth2;
|
|
14340
|
-
let xMax2=zone2.x+demiWidth2;
|
|
14341
|
-
|
|
14342
|
-
//OLD : let widthsOverlap=((xMin2<xMin1 && xMin1<xMax2) || (xMin2<xMax1 && xMax1<xMax2)
|
|
14343
|
-
// || (xMin1<xMin2 && xMin2<xMax1) || (xMin1<xMax2 && xMax2<xMax1));
|
|
14344
|
-
|
|
14345
|
-
let widthsOverlap=(xMin1<xMax2 && xMin2<xMax1);
|
|
14346
|
-
|
|
14347
|
-
|
|
14348
|
-
let demiHeight1=zone1.h*.5;
|
|
14349
|
-
let demiHeight2=zone2.h*.5;
|
|
14350
|
-
|
|
14351
|
-
let yMin1=zone1.y-demiHeight1;
|
|
14352
|
-
let yMax1=zone1.y+demiHeight1;
|
|
14353
|
-
let yMin2=zone2.y-demiHeight2;
|
|
14354
|
-
let yMax2=zone2.y+demiHeight2;
|
|
14355
|
-
|
|
14356
|
-
//OLD : let heightsOverlap=((yMin1<yMin2 && yMin2<yMax1) || (yMin1<yMax2 && yMax2<yMax1)
|
|
14357
|
-
// || (yMin2<yMin1 && yMin1<yMax2) || (yMin2<yMax1 && yMax1<yMax2));
|
|
14358
|
-
|
|
14359
|
-
let heightsOverlap=(yMin1<yMax2 && yMin2<yMax1);
|
|
14360
|
-
|
|
14361
|
-
return widthsOverlap && heightsOverlap;
|
|
14362
|
-
}
|
|
14363
|
-
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
14174
|
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
14367
14175
|
/*public static*/window.getIntersectionZone=function(zone1, zone2){
|
|
14368
14176
|
if(!isZoneCollidingWithZone(zone1, zone2)) return null;
|
|
@@ -14557,10 +14365,10 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
14557
14365
|
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
14558
14366
|
|
|
14559
14367
|
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
14560
|
-
/*utils 3D library associated with aotra version : «1_29072022-2359 (
|
|
14368
|
+
/*utils 3D library associated with aotra version : «1_29072022-2359 (02/06/2026-01:05:39)»*/
|
|
14561
14369
|
/*-----------------------------------------------------------------------------*/
|
|
14562
14370
|
|
|
14563
|
-
/*utils AI library associated with aotra version : «1_29072022-2359 (
|
|
14371
|
+
/*utils AI library associated with aotra version : «1_29072022-2359 (02/06/2026-01:05:39)»*/
|
|
14564
14372
|
/*-----------------------------------------------------------------------------*/
|
|
14565
14373
|
|
|
14566
14374
|
|
|
@@ -14706,11 +14514,11 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
14706
14514
|
|
|
14707
14515
|
|
|
14708
14516
|
|
|
14709
|
-
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (
|
|
14517
|
+
/*utils CONSOLE library associated with aotra version : «1_29072022-2359 (02/06/2026-01:05:39)»*/
|
|
14710
14518
|
/*-----------------------------------------------------------------------------*/
|
|
14711
14519
|
|
|
14712
14520
|
|
|
14713
|
-
/* ## Utility methods in a javascript, for AORTAC subsystem (
|
|
14521
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (client)
|
|
14714
14522
|
*
|
|
14715
14523
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
14716
14524
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -14733,163 +14541,584 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
14733
14541
|
if(typeof(window)==="undefined") window=global;
|
|
14734
14542
|
|
|
14735
14543
|
|
|
14544
|
+
|
|
14545
|
+
|
|
14736
14546
|
// ==================================================================================================================
|
|
14737
14547
|
|
|
14738
14548
|
|
|
14739
|
-
// AORTAC
|
|
14549
|
+
// AORTAC CLIENT
|
|
14550
|
+
|
|
14740
14551
|
|
|
14741
14552
|
|
|
14742
14553
|
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
14743
14554
|
|
|
14744
|
-
|
|
14555
|
+
const AORTAC_CLIENT_FORCE_SSL_USAGE=true;
|
|
14745
14556
|
|
|
14746
14557
|
// New implementation :
|
|
14747
|
-
|
|
14748
|
-
class
|
|
14558
|
+
|
|
14559
|
+
class AORTACClientCell{
|
|
14749
14560
|
|
|
14750
|
-
constructor(
|
|
14561
|
+
constructor(serverCellOrigin, model, view, isNodeContext=false){
|
|
14562
|
+
|
|
14563
|
+
this.clientId=getUUID();
|
|
14751
14564
|
|
|
14752
14565
|
this.model=model;
|
|
14753
|
-
this.
|
|
14754
|
-
|
|
14755
|
-
this.quorumNumber=quorumNumber;
|
|
14756
|
-
this.selfOrigin=selfOrigin;
|
|
14757
|
-
const infos=splitURL(this.selfOrigin);
|
|
14758
|
-
|
|
14759
|
-
//DBG
|
|
14760
|
-
lognow("infos:::::::::::::",infos);
|
|
14566
|
+
this.view=view;
|
|
14567
|
+
this.isNodeContext=isNodeContext;
|
|
14761
14568
|
|
|
14569
|
+
const infos=splitURL(serverCellOrigin);
|
|
14570
|
+
|
|
14762
14571
|
this.protocol=nonull(infos.protocol,"ws");
|
|
14763
14572
|
this.host=nonull(infos.host,"localhost");
|
|
14764
14573
|
this.port=nonull(infos.port,"30000");
|
|
14765
|
-
this.
|
|
14766
|
-
|
|
14767
|
-
this.server=null;
|
|
14768
|
-
this.incomingCells={};
|
|
14574
|
+
this.usesSSL=AORTAC_CLIENT_FORCE_SSL_USAGE;
|
|
14769
14575
|
|
|
14770
|
-
this.
|
|
14771
|
-
this.outcomingCells={};
|
|
14772
|
-
const self=this;
|
|
14773
|
-
foreach(this.outcomingCellsOrigins,outcomingCellOrigin=>{
|
|
14774
|
-
self.outcomingCells[outcomingCellOrigin]={connected:false,client:null};
|
|
14775
|
-
});
|
|
14776
|
-
this.outcomingNetworkProber=null;
|
|
14576
|
+
this.startTime=null;
|
|
14777
14577
|
|
|
14778
|
-
this.
|
|
14779
|
-
|
|
14780
|
-
"outcoming":{"protocol":{}},
|
|
14781
|
-
"both":{"protocol":{}},
|
|
14782
|
-
};
|
|
14783
|
-
|
|
14784
|
-
this.cellsOverview={};
|
|
14578
|
+
this.initialClientInstance=null;
|
|
14579
|
+
this.clientInstances={};
|
|
14785
14580
|
|
|
14786
|
-
this.startTime=null;
|
|
14787
14581
|
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
this.
|
|
14582
|
+
|
|
14583
|
+
this.ready=false;
|
|
14584
|
+
|
|
14585
|
+
this.lobuleZone=new ClientLobule();
|
|
14792
14586
|
|
|
14587
|
+
|
|
14793
14588
|
}
|
|
14589
|
+
|
|
14590
|
+
|
|
14591
|
+
start(manifestationZone){
|
|
14592
|
+
|
|
14593
|
+
const self=this;
|
|
14794
14594
|
|
|
14795
|
-
start(){
|
|
14796
14595
|
|
|
14596
|
+
this.manifestationZone=manifestationZone;
|
|
14797
14597
|
this.startTime=getNow();
|
|
14798
|
-
this.server=this.launchServerForIncomingConnections();
|
|
14799
|
-
|
|
14800
|
-
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
14801
|
-
// TRACE
|
|
14802
|
-
lognow("ERROR : Server cell must have an outcoming link to at least one other server cell of the blob. Aborting outcoming network probing.");
|
|
14803
|
-
}else{
|
|
14804
|
-
this.outcomingNetworkProber=new PeriodicalExecuter(this.probeOutcomingNetwork, AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS, this);
|
|
14805
|
-
}
|
|
14806
|
-
|
|
14807
|
-
this.handleCommonListeners();
|
|
14808
|
-
|
|
14809
|
-
// TRACE
|
|
14810
|
-
lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
|
|
14811
14598
|
|
|
14812
|
-
return this;
|
|
14813
|
-
}
|
|
14814
|
-
|
|
14815
|
-
/*private*/launchServerForIncomingConnections(){
|
|
14816
|
-
const self=this;
|
|
14817
14599
|
|
|
14818
|
-
const server=initNodeServerInfrastructureWrapper(
|
|
14819
|
-
// On each client connection :
|
|
14820
|
-
null,
|
|
14821
|
-
// On client finalization :
|
|
14822
|
-
function(server){
|
|
14823
14600
|
|
|
14824
|
-
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
14830
|
-
|
|
14831
|
-
|
|
14832
|
-
self.executeListeners("both",channelName, message, clientSocket);
|
|
14833
|
-
});
|
|
14834
|
-
});
|
|
14601
|
+
return new Promise((resolve,reject)=>{
|
|
14602
|
+
|
|
14603
|
+
|
|
14604
|
+
// 1) Initial connection
|
|
14605
|
+
self.initialClientInstance=initClient(self.isNodeContext, false, (socketToServerClientInstance, initialClientInstanceLocal)=>{
|
|
14606
|
+
|
|
14607
|
+
// DBG
|
|
14608
|
+
lognow("DEBUG : Starting client...");
|
|
14835
14609
|
|
|
14610
|
+
initialClientInstanceLocal.socketToServerClientInstance=socketToServerClientInstance;
|
|
14836
14611
|
|
|
14837
|
-
//
|
|
14838
|
-
self.
|
|
14612
|
+
// INITIATE CLIENT HELLO SEQUENCE
|
|
14613
|
+
self.initiateClientHelloSequence();
|
|
14839
14614
|
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
14615
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
14616
|
+
self.handleClientHelloSequence(resolve);
|
|
14617
|
+
|
|
14618
|
+
|
|
14619
|
+
}, self.protocol+"://"+self.host, self.port, self.usesSSL);
|
|
14620
|
+
self.initialClientInstance.client.start();
|
|
14621
|
+
|
|
14622
|
+
});
|
|
14843
14623
|
}
|
|
14844
14624
|
|
|
14845
|
-
/*private*/probeOutcomingNetwork(){
|
|
14846
|
-
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
14847
|
-
let numberOfConnectedOutcomingServers=0;
|
|
14848
|
-
foreach(this.outcomingCells, outcomingCell=>{
|
|
14849
|
-
numberOfConnectedOutcomingServers++
|
|
14850
|
-
},outcomingCell=>outcomingCell.connected);
|
|
14851
|
-
|
|
14852
|
-
// TRACE
|
|
14853
|
-
lognow(`INFO : Number of connected outcoming servers : ${numberOfConnectedOutcomingServers}/${numberOfTotalServers}`);
|
|
14854
|
-
|
|
14855
|
-
if(numberOfTotalServers<=numberOfConnectedOutcomingServers){
|
|
14856
|
-
this.outcomingNetworkProber.stop();
|
|
14857
|
-
|
|
14858
|
-
// TRACE
|
|
14859
|
-
lognow(`INFO : All outcoming cells connected. Stopping outcoming network probing.`);
|
|
14860
14625
|
|
|
14861
|
-
|
|
14862
|
-
|
|
14626
|
+
// ===================================================================================================
|
|
14627
|
+
// SEQUENCES
|
|
14628
|
+
// ===================================================================================================
|
|
14863
14629
|
|
|
14864
|
-
|
|
14865
|
-
|
|
14630
|
+
|
|
14631
|
+
// ==========================================================
|
|
14632
|
+
// CLIENT HELLO SEQUENCE
|
|
14633
|
+
|
|
14634
|
+
// 1) We send a client hello request
|
|
14635
|
+
// 2) Server cell receives it and asks the blob for all servers which this client must reconnect to
|
|
14636
|
+
// (also server sends its objects corresponding to zone, if it has some of them)
|
|
14637
|
+
// 3) We receive the confirmation and reconnect to all the indicated servers
|
|
14638
|
+
// 4) Each concerned server cell receives it and registers the client and sends the hello confirmation
|
|
14639
|
+
// (and also the objects in the zone it had stored when trying to know if it's a concerned server or not)
|
|
14640
|
+
// 5) We have all the objects, we can initialize the client model.
|
|
14641
|
+
|
|
14642
|
+
|
|
14643
|
+
/*private*/initiateClientHelloSequence(){
|
|
14644
|
+
// 1- We want to present ourselves to server cell :
|
|
14645
|
+
const clientHelloRequest={
|
|
14646
|
+
clientId:this.clientId,
|
|
14647
|
+
type:"clientHello",
|
|
14648
|
+
manifestationZone:this.manifestationZone
|
|
14649
|
+
};
|
|
14650
|
+
this.initialClientInstance.socketToServerClientInstance.send("protocol", clientHelloRequest);
|
|
14651
|
+
}
|
|
14866
14652
|
|
|
14867
|
-
|
|
14653
|
+
/*private*/handleClientHelloSequence(resolve){
|
|
14868
14654
|
|
|
14869
|
-
|
|
14870
|
-
|
|
14655
|
+
const self=this;
|
|
14656
|
+
this.initialClientInstance.socketToServerClientInstance.receive("protocol", (messageParam, clientSocket)=>{
|
|
14871
14657
|
|
|
14872
|
-
const
|
|
14658
|
+
const message=JSON.recycle(messageParam);
|
|
14659
|
+
|
|
14660
|
+
const servers=message.servers;
|
|
14661
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
14662
|
+
const objects=message.objects;
|
|
14663
|
+
|
|
14664
|
+
lognow("DEBUG : Client received its servers :",servers);
|
|
14665
|
+
lognow("DEBUG : Client received some objects :",objects);
|
|
14666
|
+
|
|
14667
|
+
|
|
14668
|
+
self.lobuleZone.resetServersNumbers();
|
|
14669
|
+
self.lobuleZone.append(servers, "serversBag");
|
|
14670
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST ITS ROOTCONTAINER (during the «client hello» phase) !!!
|
|
14671
|
+
self.lobuleZone.append(objects);
|
|
14672
|
+
|
|
14673
|
+
const lobuleObjects=self.lobuleZone.getObjects();
|
|
14674
|
+
if(!servers || empty(servers)){
|
|
14675
|
+
// TRACE
|
|
14676
|
+
lognow("ERROR : Servers blob sent no servers to reconnect to. Aborting.");
|
|
14677
|
+
// (We only return the structural object, ie. the root container :)
|
|
14678
|
+
resolve(lobuleObjects);
|
|
14679
|
+
return;
|
|
14680
|
+
}
|
|
14681
|
+
|
|
14682
|
+
if(empty(servers)){
|
|
14683
|
+
// TRACE
|
|
14684
|
+
lognow("ERROR : No servers to connect to. Aborting (not initiaing the reconnection sequence).");
|
|
14685
|
+
// (We only return the structural object, ie. the root container :)
|
|
14686
|
+
resolve(lobuleObjects);
|
|
14687
|
+
return;
|
|
14688
|
+
}
|
|
14689
|
+
|
|
14690
|
+
// 2) Re-connections :
|
|
14691
|
+
foreach(servers, serverOrigin=>{
|
|
14692
|
+
|
|
14693
|
+
const infos=splitURL(serverOrigin);
|
|
14694
|
+
|
|
14695
|
+
const protocol=nonull(infos.protocol,"ws");
|
|
14696
|
+
const host=nonull(infos.host,"localhost");
|
|
14697
|
+
const port=nonull(infos.port,"30000");
|
|
14698
|
+
const usesSSL=AORTAC_CLIENT_FORCE_SSL_USAGE;
|
|
14699
|
+
|
|
14700
|
+
const reconnectedClientInstance=initClient(self.isNodeContext, false, (socketToServerClientInstance, reconnectedClientInstanceLocal)=>{
|
|
14701
|
+
|
|
14702
|
+
// DBG
|
|
14703
|
+
lognow("DEBUG : Starting reconnected client...");
|
|
14704
|
+
|
|
14705
|
+
reconnectedClientInstanceLocal.socketToServerClientInstance=socketToServerClientInstance;
|
|
14706
|
+
|
|
14707
|
+
// INITIATE CLIENT RECONNECTED HELLO SEQUENCE
|
|
14708
|
+
self.initiateReconnectedClientHelloSequence(reconnectedClientInstance);
|
|
14709
|
+
|
|
14710
|
+
// HANDLE CLIENT RECONNECTED HELLO SEQUENCE
|
|
14711
|
+
self.handleReconnectedClientHelloSequence(reconnectedClientInstance, resolve)
|
|
14712
|
+
|
|
14713
|
+
|
|
14714
|
+
}, protocol+"://"+host, port, usesSSL);
|
|
14715
|
+
|
|
14716
|
+
self.clientInstances[serverOrigin]=reconnectedClientInstance;
|
|
14717
|
+
reconnectedClientInstance.client.start();
|
|
14718
|
+
});
|
|
14719
|
+
|
|
14720
|
+
|
|
14721
|
+
},{listenerMessageType:"clientHello.response"});
|
|
14722
|
+
|
|
14723
|
+
}
|
|
14724
|
+
|
|
14725
|
+
// ----
|
|
14726
|
+
// RECONNECTED CLIENT HELLO SEQUENCE
|
|
14727
|
+
|
|
14728
|
+
|
|
14729
|
+
/*private*/initiateReconnectedClientHelloSequence(clientInstance){
|
|
14730
|
+
|
|
14731
|
+
// We want to present ourselves to server cell :
|
|
14732
|
+
const reconnectedClientHelloRequest={
|
|
14733
|
+
clientId:this.clientId,
|
|
14734
|
+
type:"reconnectedClientHello"
|
|
14735
|
+
};
|
|
14736
|
+
clientInstance.client.socketToServerClientInstance.send("protocol", reconnectedClientHelloRequest);
|
|
14737
|
+
|
|
14738
|
+
}
|
|
14739
|
+
|
|
14740
|
+
/*private*/handleReconnectedClientHelloSequence(clientInstance, resolve){
|
|
14741
|
+
|
|
14742
|
+
|
|
14743
|
+
const self=this;
|
|
14744
|
+
|
|
14745
|
+
clientInstance.client.socketToServerClientInstance.receive("protocol", (messageParam, clientSocket)=>{
|
|
14746
|
+
|
|
14747
|
+
const message=JSON.recycle(messageParam);
|
|
14748
|
+
|
|
14749
|
+
const objects=message.objects;
|
|
14750
|
+
|
|
14751
|
+
if(!objects || empty(objects)){
|
|
14752
|
+
// TRACE
|
|
14753
|
+
lognow("ERROR : This server cell sent no objects. Continuing.");
|
|
14754
|
+
|
|
14755
|
+
// DBG
|
|
14756
|
+
lognow("DEBUG : !!!!!!!!!!!!! message:",message);
|
|
14757
|
+
|
|
14758
|
+
}
|
|
14759
|
+
|
|
14760
|
+
lognow("DEBUG : Client received some objects :",objects);
|
|
14761
|
+
|
|
14762
|
+
// We accumulate the received objects in the lobule zone :
|
|
14763
|
+
self.lobuleZone.append(objects, "objectsBag");
|
|
14764
|
+
|
|
14765
|
+
self.lobuleZone.incrementContactedServersNumber();
|
|
14766
|
+
|
|
14767
|
+
|
|
14768
|
+
// DBG
|
|
14769
|
+
lognow("DEBUG : !!!!!!!!!!!!! self.lobuleZone.getObjects():",self.lobuleZone.getObjects());
|
|
14770
|
+
|
|
14771
|
+
|
|
14772
|
+
if(self.lobuleZone.hasReceivedFromAllServers()){
|
|
14773
|
+
|
|
14774
|
+
const lobuleObjects=self.lobuleZone.getObjects();
|
|
14775
|
+
if(!lobuleObjects || empty(lobuleObjects)){
|
|
14776
|
+
// TRACE
|
|
14777
|
+
lognow("ERROR : No objects at all accumulated from server cells blob. Aborting.");
|
|
14778
|
+
resolve(null);
|
|
14779
|
+
return;
|
|
14780
|
+
}
|
|
14781
|
+
|
|
14782
|
+
// const allClientModelObjectsByClassName=self.sortByClassName(lobuleObjects);
|
|
14783
|
+
const allClientModelObjects=lobuleObjects;
|
|
14784
|
+
|
|
14785
|
+
//DBG
|
|
14786
|
+
// lognow("DEBUG : allClientModelObjectsByClassName : ",allClientModelObjectsByClassName);
|
|
14787
|
+
lognow("DEBUG : allClientModelObjects : ",allClientModelObjects);
|
|
14788
|
+
|
|
14789
|
+
if(empty(allClientModelObjects)){
|
|
14790
|
+
// TRACE
|
|
14791
|
+
lognow("ERROR : Servers blob sent no game level. Aborting.");
|
|
14792
|
+
resolve(allClientModelObjects);
|
|
14793
|
+
return;
|
|
14794
|
+
}
|
|
14795
|
+
|
|
14796
|
+
// DBG
|
|
14797
|
+
lognow("DEBUG : Client received its model objects:",message.objects);
|
|
14798
|
+
// lognow("DEBUG : Client received its model objects (after sorting):",allClientModelObjectsByClassName);
|
|
14799
|
+
|
|
14800
|
+
// DBG
|
|
14801
|
+
lognow("DEBUG : Client has received all its objects from the servers cells blob: ");
|
|
14802
|
+
|
|
14803
|
+
resolve(allClientModelObjects);
|
|
14804
|
+
}
|
|
14805
|
+
|
|
14806
|
+
|
|
14807
|
+
},{listenerMessageType:"reconnectedClientHello.response"});
|
|
14808
|
+
|
|
14809
|
+
}
|
|
14810
|
+
|
|
14811
|
+
|
|
14812
|
+
|
|
14813
|
+
// ================================================================================
|
|
14814
|
+
|
|
14815
|
+
|
|
14816
|
+
|
|
14817
|
+
|
|
14818
|
+
}
|
|
14819
|
+
|
|
14820
|
+
|
|
14821
|
+
|
|
14822
|
+
|
|
14823
|
+
class ClientLobule{
|
|
14824
|
+
|
|
14825
|
+
constructor(){
|
|
14826
|
+
this.serversBag=null;
|
|
14827
|
+
this.objectsBag=null;
|
|
14828
|
+
this.numberOfContactedServers=0;
|
|
14829
|
+
this.totalNumberOfServersToContact=0;
|
|
14830
|
+
this.clear();
|
|
14831
|
+
}
|
|
14832
|
+
append(objs, attributeName="objectsBag"){
|
|
14833
|
+
const self=this;
|
|
14834
|
+
if(!this[attributeName])
|
|
14835
|
+
this[attributeName]=[];
|
|
14836
|
+
foreach(objs,obj=>{
|
|
14837
|
+
self[attributeName].push(obj);
|
|
14838
|
+
},(obj)=>(!contains(this[attributeName], obj)));
|
|
14839
|
+
if(attributeName=="serversBag")
|
|
14840
|
+
this.totalNumberOfServersToContact+=objs.length;
|
|
14841
|
+
}
|
|
14842
|
+
getObjects(filterFunction=null){
|
|
14843
|
+
if(!filterFunction) return this.objectsBag;
|
|
14844
|
+
if(!this.objectsBag) return null;
|
|
14845
|
+
const results=[];
|
|
14846
|
+
foreach(this.objectsBag, (obj)=>{
|
|
14847
|
+
results.push(obj);
|
|
14848
|
+
},filterFunction);
|
|
14849
|
+
return results;
|
|
14850
|
+
}
|
|
14851
|
+
getServers(){
|
|
14852
|
+
if(!this.serversBag) return null;
|
|
14853
|
+
return this.serversBag;
|
|
14854
|
+
}
|
|
14855
|
+
clear(attributeName=null){
|
|
14856
|
+
if(!attributeName){
|
|
14857
|
+
this.serversBag=[];
|
|
14858
|
+
this.objectsBag=[];
|
|
14859
|
+
}else{
|
|
14860
|
+
this[attributeName]=[];
|
|
14861
|
+
}
|
|
14862
|
+
this.resetServersNumbers();
|
|
14863
|
+
return this;
|
|
14864
|
+
}
|
|
14865
|
+
resetServersNumbers(){
|
|
14866
|
+
this.numberOfContactedServers=0;
|
|
14867
|
+
this.totalNumberOfServersToContact=0;
|
|
14868
|
+
}
|
|
14869
|
+
incrementContactedServersNumber(increment=1){
|
|
14870
|
+
this.numberOfContactedServers+=increment;
|
|
14871
|
+
}
|
|
14872
|
+
hasReceivedFromAllServers(){
|
|
14873
|
+
return (this.totalNumberOfServersToContact<=this.numberOfContactedServers);
|
|
14874
|
+
}
|
|
14875
|
+
}
|
|
14876
|
+
|
|
14877
|
+
|
|
14878
|
+
|
|
14879
|
+
|
|
14880
|
+
|
|
14881
|
+
|
|
14882
|
+
window.getAORTACClient=function(serverCellOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
14883
|
+
if(nothing(serverCellOrigin)){
|
|
14884
|
+
// TRACE
|
|
14885
|
+
lognow("ERROR : No known server node, cannot connect to servers cells blob. Aborting AORTAC client setup.");
|
|
14886
|
+
return null;
|
|
14887
|
+
}
|
|
14888
|
+
return new AORTACClientCell(serverCellOrigin, model, view, isNodeContext);
|
|
14889
|
+
}
|
|
14890
|
+
|
|
14891
|
+
|
|
14892
|
+
|
|
14893
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (server)
|
|
14894
|
+
*
|
|
14895
|
+
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
14896
|
+
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
14897
|
+
* Feel free to use/modify-enhance/publish them under the terms of its license.
|
|
14898
|
+
*
|
|
14899
|
+
* # Library name : «aotrautils»
|
|
14900
|
+
* # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
|
|
14901
|
+
* # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
|
|
14902
|
+
* # Author email : info@alqemia.com
|
|
14903
|
+
* # Organization name : Alqemia
|
|
14904
|
+
* # Organization email : admin@alqemia.com
|
|
14905
|
+
* # Organization website : https://alqemia.com
|
|
14906
|
+
*
|
|
14907
|
+
*
|
|
14908
|
+
*/
|
|
14909
|
+
|
|
14910
|
+
|
|
14911
|
+
|
|
14912
|
+
// COMPATIBILITY browser javascript / nodejs environment :
|
|
14913
|
+
if(typeof(window)==="undefined") window=global;
|
|
14914
|
+
|
|
14915
|
+
|
|
14916
|
+
// ==================================================================================================================
|
|
14917
|
+
|
|
14918
|
+
|
|
14919
|
+
// AORTAC SERVER
|
|
14920
|
+
|
|
14921
|
+
|
|
14922
|
+
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
14923
|
+
|
|
14924
|
+
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
|
14925
|
+
|
|
14926
|
+
AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS=2000;
|
|
14927
|
+
|
|
14928
|
+
class AORTACServerCell{
|
|
14929
|
+
|
|
14930
|
+
constructor(quorumNumber=1, selfOrigin, outcomingCellsOrigins, model, controller, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
|
14931
|
+
|
|
14932
|
+
this.model=model;
|
|
14933
|
+
this.controller=controller;
|
|
14934
|
+
|
|
14935
|
+
this.quorumNumber=quorumNumber;
|
|
14936
|
+
this.selfOrigin=selfOrigin;
|
|
14937
|
+
const infos=splitURL(this.selfOrigin);
|
|
14938
|
+
|
|
14939
|
+
//DBG
|
|
14940
|
+
lognow("(SERVER) infos:::::::::::::",infos);
|
|
14941
|
+
|
|
14942
|
+
this.protocol=nonull(infos.protocol,"ws");
|
|
14943
|
+
this.host=nonull(infos.host,"localhost");
|
|
14944
|
+
this.port=nonull(infos.port,"30000");
|
|
14945
|
+
this.sslConfig=sslConfig;
|
|
14946
|
+
|
|
14947
|
+
this.serverWrapper=null;
|
|
14948
|
+
this.incomingCells={};
|
|
14949
|
+
|
|
14950
|
+
this.outcomingCellsOrigins=outcomingCellsOrigins;
|
|
14951
|
+
this.outcomingCells={};
|
|
14952
|
+
const self=this;
|
|
14953
|
+
foreach(this.outcomingCellsOrigins,outcomingCellOrigin=>{
|
|
14954
|
+
self.outcomingCells[outcomingCellOrigin]={connected:false,client:null};
|
|
14955
|
+
});
|
|
14956
|
+
this.outcomingNetworkProber=null;
|
|
14957
|
+
|
|
14958
|
+
this.onReceiveMessageListeners={
|
|
14959
|
+
"incoming":{"protocol":{}},
|
|
14960
|
+
"outcoming":{"protocol":{}},
|
|
14961
|
+
"both":{"protocol":{}},
|
|
14962
|
+
};
|
|
14963
|
+
|
|
14964
|
+
this.cellsOverview={};
|
|
14965
|
+
this.numberOfPartitions=null;
|
|
14966
|
+
this.partitionId=null;
|
|
14967
|
+
|
|
14968
|
+
this.isServerCellQuorumLastCell=false;
|
|
14969
|
+
|
|
14970
|
+
this.startTime=null;
|
|
14971
|
+
|
|
14972
|
+
this.quorumIsReachedSequenceIsInitiated=false;
|
|
14973
|
+
// this.quorumCells=null; // (CAUTION : only the latest ready cell when quorum is reached has this attribute populated)
|
|
14974
|
+
|
|
14975
|
+
this.lobuleZone=new ServerCellLobule();
|
|
14976
|
+
|
|
14977
|
+
this.clients={};
|
|
14978
|
+
|
|
14979
|
+
}
|
|
14980
|
+
|
|
14981
|
+
start(){
|
|
14982
|
+
|
|
14983
|
+
this.startTime=getNow();
|
|
14984
|
+
this.serverWrapper=this.launchServerForIncomingConnections();
|
|
14985
|
+
|
|
14986
|
+
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
14987
|
+
// TRACE
|
|
14988
|
+
lognow("ERROR : Server cell must have an outcoming link to at least one other server cell of the blob. Aborting outcoming network probing.");
|
|
14989
|
+
}else{
|
|
14990
|
+
this.outcomingNetworkProber=new PeriodicalExecuter(this.probeOutcomingNetwork, AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS, this);
|
|
14991
|
+
}
|
|
14992
|
+
|
|
14993
|
+
this.handleCommonListeners();
|
|
14994
|
+
|
|
14995
|
+
// TRACE
|
|
14996
|
+
lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
|
|
14997
|
+
|
|
14998
|
+
return this;
|
|
14999
|
+
}
|
|
15000
|
+
|
|
15001
|
+
|
|
15002
|
+
// ===================================================================================================
|
|
15003
|
+
// SERVER CELL CORE
|
|
15004
|
+
// ===================================================================================================
|
|
15005
|
+
|
|
15006
|
+
/*private*/launchServerForIncomingConnections(){
|
|
15007
|
+
const self=this;
|
|
15008
|
+
|
|
15009
|
+
const serverWrapper=initNodeServerInfrastructureWrapper(
|
|
15010
|
+
// On each client connection :
|
|
15011
|
+
null,
|
|
15012
|
+
// On client finalization :
|
|
15013
|
+
function(server){
|
|
15014
|
+
|
|
15015
|
+
|
|
15016
|
+
const channelsNames=[];
|
|
15017
|
+
foreach(Object.keys(self.onReceiveMessageListeners["incoming"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
15018
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
15019
|
+
|
|
15020
|
+
|
|
15021
|
+
// On-receive message listeners handling ;
|
|
15022
|
+
foreach(channelsNames, channelName=>{
|
|
15023
|
+
server.receive(channelName, (message, clientSocket)=>{
|
|
15024
|
+
|
|
15025
|
+
// DBG
|
|
15026
|
+
lognow("DEBUG : Server cell receives message from incoming...");
|
|
15027
|
+
|
|
15028
|
+
self.executeListeners("incoming", channelName, message, clientSocket);
|
|
15029
|
+
|
|
15030
|
+
// DBG
|
|
15031
|
+
lognow("DEBUG : Server cell receives message from both (in)...");
|
|
15032
|
+
|
|
15033
|
+
self.executeListeners("both",channelName, message, clientSocket);
|
|
15034
|
+
});
|
|
15035
|
+
});
|
|
15036
|
+
|
|
15037
|
+
|
|
15038
|
+
// HANDLE SERVER CELL HELLO SEQUENCE
|
|
15039
|
+
self.handleServerCellHelloSequence();
|
|
15040
|
+
|
|
15041
|
+
|
|
15042
|
+
// ******************************************
|
|
15043
|
+
// CLIENT
|
|
15044
|
+
|
|
15045
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
15046
|
+
self.handleClientHelloSequence();
|
|
15047
|
+
|
|
15048
|
+
|
|
15049
|
+
},
|
|
15050
|
+
// On client connection lost :
|
|
15051
|
+
(clientId)=>{
|
|
15052
|
+
lognow(`INFO : Connection to client id «${clientId}» was lost.`);
|
|
15053
|
+
// TRACE
|
|
15054
|
+
lognow(`INFO : Removing client information for client id «${clientId}».`);
|
|
15055
|
+
self.lobuleZone.remove(clientId);
|
|
15056
|
+
},
|
|
15057
|
+
this.port, this.sslConfig.certPath, this.sslConfig.keyPath);
|
|
15058
|
+
|
|
15059
|
+
serverWrapper.serverManager.start();
|
|
15060
|
+
return serverWrapper;
|
|
15061
|
+
}
|
|
15062
|
+
|
|
15063
|
+
/*private*/probeOutcomingNetwork(){
|
|
15064
|
+
|
|
15065
|
+
const self=this;
|
|
15066
|
+
|
|
15067
|
+
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
15068
|
+
let numberOfConnectedOutcomingServers=0;
|
|
15069
|
+
foreach(this.outcomingCells, outcomingCell=>{
|
|
15070
|
+
numberOfConnectedOutcomingServers++
|
|
15071
|
+
},outcomingCell=>outcomingCell.connected);
|
|
15072
|
+
|
|
15073
|
+
// TRACE
|
|
15074
|
+
lognow(`INFO : Number of connected outcoming servers : ${numberOfConnectedOutcomingServers}/${numberOfTotalServers}`);
|
|
15075
|
+
|
|
15076
|
+
if(numberOfTotalServers<=numberOfConnectedOutcomingServers){
|
|
15077
|
+
this.outcomingNetworkProber.stop();
|
|
15078
|
+
|
|
15079
|
+
// TRACE
|
|
15080
|
+
lognow(`INFO : All outcoming cells connected. Stopping outcoming network probing.`);
|
|
15081
|
+
|
|
15082
|
+
// INITIATE CELL IS READY SEQUENCE
|
|
15083
|
+
this.initiateCellIsReadySequence();
|
|
15084
|
+
|
|
15085
|
+
return;
|
|
15086
|
+
}
|
|
15087
|
+
|
|
15088
|
+
|
|
15089
|
+
// We try to connect to all outcoming cells :
|
|
15090
|
+
foreach(this.outcomingCells, (outcomingCell, outcomingCellOrigin)=>{
|
|
15091
|
+
|
|
15092
|
+
const infos=splitURL(outcomingCellOrigin);
|
|
14873
15093
|
const outcomingCellProtocol=nonull(infos.protocol,"ws");
|
|
14874
15094
|
const outcomingCellHost=nonull(infos.host,"localhost");
|
|
14875
15095
|
const outcomingCellPort=nonull(infos.port,"40000");
|
|
14876
15096
|
|
|
14877
15097
|
const clientInstance=initClient(true, false, (socketToServerClientInstance)=>{
|
|
14878
15098
|
|
|
15099
|
+
|
|
15100
|
+
const channelsNames=[];
|
|
15101
|
+
foreach(Object.keys(self.onReceiveMessageListeners["outcoming"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
15102
|
+
foreach(Object.keys(self.onReceiveMessageListeners["both"]), channelName=>{channelsNames.push(channelName);}, channelName=>!contains(channelsNames,channelName));
|
|
15103
|
+
|
|
15104
|
+
|
|
14879
15105
|
// On-receive message listeners handling ;
|
|
14880
|
-
foreach(
|
|
15106
|
+
foreach(channelsNames, channelName=>{
|
|
14881
15107
|
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
15108
|
+
|
|
15109
|
+
// DBG
|
|
15110
|
+
lognow("DEBUG : Server cell receives message from outcoming...");
|
|
15111
|
+
|
|
14882
15112
|
self.executeListeners("outcoming",channelName, message, clientSocket);
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
15113
|
+
|
|
15114
|
+
// DBG
|
|
15115
|
+
lognow("DEBUG : Server cell receives message from both (out)...");
|
|
15116
|
+
|
|
14887
15117
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
14888
15118
|
});
|
|
14889
15119
|
});
|
|
14890
|
-
|
|
14891
15120
|
|
|
14892
|
-
// INITIATE HELLO SEQUENCE
|
|
15121
|
+
// INITIATE SERVER CELL HELLO SEQUENCE
|
|
14893
15122
|
self.initiateHelloSequence(socketToServerClientInstance);
|
|
14894
15123
|
|
|
14895
15124
|
// We are connected:
|
|
@@ -14913,47 +15142,68 @@ class AORTACServerCell{
|
|
|
14913
15142
|
|
|
14914
15143
|
}
|
|
14915
15144
|
|
|
14916
|
-
|
|
14917
15145
|
/*private*/handleCommonListeners(){
|
|
14918
|
-
|
|
14919
15146
|
// HANDLE CELL IS READY SEQUENCE
|
|
14920
15147
|
this.handleCellIsReadySequence();
|
|
14921
|
-
|
|
14922
15148
|
// HANDLE PARTITION SEQUENCE
|
|
14923
15149
|
this.handlePartitionSequence();
|
|
14924
15150
|
|
|
14925
|
-
|
|
15151
|
+
|
|
15152
|
+
// ******************************************
|
|
15153
|
+
// CLIENT
|
|
15154
|
+
// HANDLE CLIENT MODEL INITIAL POPULATION SEQUENCE
|
|
15155
|
+
this.handleGetServersForZoneRequest();
|
|
15156
|
+
|
|
15157
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
15158
|
+
this.handleReconnectedClientHelloSequence();
|
|
14926
15159
|
|
|
15160
|
+
}
|
|
15161
|
+
|
|
14927
15162
|
|
|
14928
15163
|
/*private*/executeListeners(outletName, channelName, message, clientSocket){
|
|
14929
15164
|
const onReceiveMessageListeners=this.onReceiveMessageListeners[outletName][channelName];
|
|
14930
15165
|
if(!onReceiveMessageListeners) return;
|
|
14931
15166
|
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
15167
|
+
const self=this;
|
|
15168
|
+
foreach(onReceiveMessageListeners, (listener, listenerMessageType)=>{
|
|
15169
|
+
listener.execute(self, message, self.serverWrapper, clientSocket);
|
|
15170
|
+
},(listener)=>(message.type===listener.listenerMessageType));
|
|
15171
|
+
|
|
15172
|
+
}
|
|
15173
|
+
|
|
15174
|
+
|
|
15175
|
+
/*private*/replyToBlobRequest(channelName, originalMessage, message){
|
|
15176
|
+
if(!message.originatingCellOrigin)
|
|
15177
|
+
message.originatingCellOrigin=originalMessage.originatingCellOrigin;
|
|
15178
|
+
if(!message.originatingPartitionId)
|
|
15179
|
+
message.originatingPartitionId=originalMessage.originatingPartitionId;
|
|
15180
|
+
if(!message.clientId)
|
|
15181
|
+
message.clientId=originalMessage.clientId;
|
|
15182
|
+
return this.sendMessageToBlob(channelName, message,
|
|
15183
|
+
{
|
|
15184
|
+
isOriginatingCell:false,
|
|
15185
|
+
destinationCellsOrigins:[originalMessage.originatingCellOrigin],
|
|
15186
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
15187
|
+
isRequest:false
|
|
15188
|
+
},
|
|
15189
|
+
originalMessage.type+".response");
|
|
14946
15190
|
}
|
|
14947
15191
|
|
|
14948
15192
|
|
|
14949
15193
|
/*private*/sendMessageToBlob(channelName, message,
|
|
14950
|
-
broadcastConfig={
|
|
15194
|
+
broadcastConfig={
|
|
15195
|
+
isOriginatingCell:false,
|
|
15196
|
+
destinationCellsOrigins:null,
|
|
15197
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
15198
|
+
isRequest:false,
|
|
15199
|
+
contactAllQuorumBlob:false
|
|
15200
|
+
},
|
|
15201
|
+
overridingMessageType=null){
|
|
14951
15202
|
|
|
14952
|
-
const self=this;
|
|
14953
|
-
|
|
14954
15203
|
if(broadcastConfig){
|
|
14955
15204
|
if(broadcastConfig.isOriginatingCell){
|
|
14956
15205
|
message.originatingCellOrigin=this.selfOrigin;
|
|
15206
|
+
message.originatingPartitionId=this.partitionId;
|
|
14957
15207
|
}
|
|
14958
15208
|
if(broadcastConfig.isRequest){
|
|
14959
15209
|
message.isRequest=true;
|
|
@@ -14966,10 +15216,36 @@ class AORTACServerCell{
|
|
|
14966
15216
|
if(!message.visitedCells)
|
|
14967
15217
|
message.visitedCells=[];
|
|
14968
15218
|
else if(contains(message.visitedCells,this.selfOrigin))
|
|
14969
|
-
return;
|
|
15219
|
+
return null;
|
|
14970
15220
|
message.visitedCells.push(this.selfOrigin);
|
|
14971
15221
|
|
|
14972
|
-
|
|
15222
|
+
|
|
15223
|
+
// AUTOMATIC LISTENER SETUP :
|
|
15224
|
+
const messageTypeForResponse=nonull(overridingMessageType, message.type+".response");
|
|
15225
|
+
let blobResponseListener=null;
|
|
15226
|
+
// 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) :
|
|
15227
|
+
if(message.isRequest && message.originatingCellOrigin==this.selfOrigin
|
|
15228
|
+
//&& (typeof(message.result)=="undefined" || message.result==null)
|
|
15229
|
+
){
|
|
15230
|
+
// To emulate a promise-like behavior :
|
|
15231
|
+
blobResponseListener=this.getBlobResponseListenerForRequest(message, channelName, broadcastConfig, messageTypeForResponse);
|
|
15232
|
+
|
|
15233
|
+
const outletName=nonull(blobResponseListener.outletName,"both");
|
|
15234
|
+
if(!this.onReceiveMessageListeners[outletName][channelName][blobResponseListener.listenerMessageType]){
|
|
15235
|
+
this.onReceiveMessageListeners[outletName][channelName][blobResponseListener.listenerMessageType]=blobResponseListener;
|
|
15236
|
+
// TRACE
|
|
15237
|
+
lognow("INFO : Added automatic listener for message type «"+blobResponseListener.listenerMessageType+"».");
|
|
15238
|
+
}else{
|
|
15239
|
+
// TRACE
|
|
15240
|
+
lognow("INFO : Listener for message type «"+blobResponseListener.listenerMessageType+"» already existed. Done nothing.");
|
|
15241
|
+
}
|
|
15242
|
+
}else{
|
|
15243
|
+
// TRACE
|
|
15244
|
+
lognow(`INFO : No listener to setup, because broadcast is not a request, or cell is not the originating cell !`);
|
|
15245
|
+
}
|
|
15246
|
+
|
|
15247
|
+
// SENDING
|
|
15248
|
+
if(broadcastConfig && !broadcastConfig.excludeIncomingServersCellsAndClientsInTransmission){
|
|
14973
15249
|
foreach(this.incomingCells,(incomingCell)=>{
|
|
14974
15250
|
// As a server, we send (forward) the message to the currently iterated upon client that is connected to us :
|
|
14975
15251
|
incomingCell.server.send(channelName, message, null, incomingCell.clientSocket);
|
|
@@ -14977,7 +15253,6 @@ class AORTACServerCell{
|
|
|
14977
15253
|
(incomingCell.connected && !contains(message.visitedCells, incomingCellOrigin))
|
|
14978
15254
|
);
|
|
14979
15255
|
}
|
|
14980
|
-
|
|
14981
15256
|
foreach(this.outcomingCells,(outcomingCell)=>{
|
|
14982
15257
|
// As a client, we send (forward) the message to the currently iterated upon server we are connected to :
|
|
14983
15258
|
outcomingCell.socketToServerClientInstance.send(channelName, message);
|
|
@@ -14985,49 +15260,84 @@ class AORTACServerCell{
|
|
|
14985
15260
|
(outcomingCell.connected && !contains(message.visitedCells, outcomingCellOrigin))
|
|
14986
15261
|
);
|
|
14987
15262
|
|
|
15263
|
+
return blobResponseListener;
|
|
15264
|
+
}
|
|
15265
|
+
|
|
15266
|
+
|
|
15267
|
+
/*private*/getBlobResponseListenerForRequest(message, channelName, broadcastConfig, messageTypeForResponse){
|
|
15268
|
+
|
|
15269
|
+
const outletName="both"; // «incoming» & «outcoming»
|
|
15270
|
+
// We want to monitor when the server cell receives the RESPONSE of the message it sent as request !!!
|
|
15271
|
+
|
|
15272
|
+
// First we check if the listener for request already exists :
|
|
15273
|
+
let blobResponseListener=this.onReceiveMessageListeners[outletName][channelName][messageTypeForResponse];
|
|
15274
|
+
if(blobResponseListener){
|
|
15275
|
+
// TRACE
|
|
15276
|
+
lognow(`INFO : Blob response listener for cell ${this.selfOrigin} on channel ${channelName} for message type «${messageTypeForResponse}» already exists.`);
|
|
15277
|
+
return blobResponseListener;
|
|
15278
|
+
}
|
|
14988
15279
|
|
|
14989
|
-
|
|
14990
|
-
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
// When we have received the message :
|
|
15010
|
-
this.thenCallback(message);
|
|
15280
|
+
|
|
15281
|
+
blobResponseListener={
|
|
15282
|
+
broadcastConfig:broadcastConfig,
|
|
15283
|
+
thenCallback:null,
|
|
15284
|
+
allBlobHasBeenContactedCallback:null,
|
|
15285
|
+
hasAllBlobBeenContacted:false,
|
|
15286
|
+
// We use the partition ids to know which server cells have been contacted :
|
|
15287
|
+
partitionIdsThatCouldBeContacted:[],
|
|
15288
|
+
// This message will be the RESPONSE to the REQUEST (ti will be of type «<messageTypeForResponse>», meaning the request message type + «.response» !!)
|
|
15289
|
+
outletName:outletName,
|
|
15290
|
+
listenerMessageType:messageTypeForResponse,
|
|
15291
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15292
|
+
|
|
15293
|
+
// 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 :
|
|
15294
|
+
|
|
15295
|
+
// When we have received the message :
|
|
15296
|
+
if(blobResponseListener.thenCallback) blobResponseListener.thenCallback(selfParam, messageParam);
|
|
15297
|
+
|
|
15298
|
+
// We try to determine if the whole blob has been contacted or not :
|
|
15299
|
+
if(blobResponseListener.broadcastConfig.contactAllQuorumBlob && !blobResponseListener.hasAllBlobBeenContacted){
|
|
15011
15300
|
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15301
|
+
if(!contains(blobResponseListener.partitionIdsThatCouldBeContacted, messageParam.originatingPartitionId))
|
|
15302
|
+
blobResponseListener.partitionIdsThatCouldBeContacted.push(messageParam.originatingPartitionId);
|
|
15303
|
+
// We need to exclude the current serevr cell itself :
|
|
15304
|
+
if(selfParam.numberOfPartitions-1<=blobResponseListener.partitionIdsThatCouldBeContacted.length){
|
|
15305
|
+
if(blobResponseListener.allBlobHasBeenContactedCallback)
|
|
15306
|
+
blobResponseListener.allBlobHasBeenContactedCallback(selfParam, messageParam);
|
|
15307
|
+
blobResponseListener.hasAllBlobBeenContacted=true;
|
|
15308
|
+
}
|
|
15017
15309
|
}
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15310
|
+
|
|
15311
|
+
// We need to remove the result listener once it has been completed though.
|
|
15312
|
+
if(!blobResponseListener.broadcastConfig.contactAllQuorumBlob || blobResponseListener.hasAllBlobBeenContacted){
|
|
15313
|
+
//UNUSEFUL : delete blobResponseListeners[listenerTypeForRequest];
|
|
15314
|
+
delete selfParam.onReceiveMessageListeners[outletName][channelName][messageTypeForResponse];
|
|
15315
|
+
}
|
|
15316
|
+
|
|
15317
|
+
},
|
|
15318
|
+
thenOnAnyResponseFromBlobRequestingCellOnly:(thenCallback)=>{
|
|
15319
|
+
blobResponseListener.thenCallback=thenCallback;
|
|
15320
|
+
return blobResponseListener;
|
|
15321
|
+
},
|
|
15322
|
+
doOnceAllBlobHasBeenContacted:(allBlobHasBeenContactedCallback)=>{
|
|
15323
|
+
blobResponseListener.allBlobHasBeenContactedCallback=allBlobHasBeenContactedCallback;
|
|
15324
|
+
return blobResponseListener;
|
|
15325
|
+
},
|
|
15326
|
+
};
|
|
15327
|
+
|
|
15025
15328
|
|
|
15329
|
+
|
|
15330
|
+
return blobResponseListener;
|
|
15026
15331
|
}
|
|
15332
|
+
|
|
15333
|
+
|
|
15027
15334
|
|
|
15335
|
+
// ===================================================================================================
|
|
15336
|
+
// SERVER CELLS
|
|
15337
|
+
// ===================================================================================================
|
|
15028
15338
|
|
|
15029
15339
|
// ==========================================================
|
|
15030
|
-
// HELLO SEQUENCE
|
|
15340
|
+
// SERVER CELL HELLO SEQUENCE
|
|
15031
15341
|
|
|
15032
15342
|
/*private*/initiateHelloSequence(socketToServerClientInstance){
|
|
15033
15343
|
|
|
@@ -15040,28 +15350,30 @@ class AORTACServerCell{
|
|
|
15040
15350
|
}
|
|
15041
15351
|
|
|
15042
15352
|
|
|
15043
|
-
/*private*/
|
|
15353
|
+
/*private*/handleServerCellHelloSequence(){
|
|
15044
15354
|
|
|
15045
15355
|
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
15356
|
+
this.onReceiveMessageListeners["both"]["protocol"]["helloRequest"]={
|
|
15357
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15049
15358
|
|
|
15050
|
-
const cellOriginToCheck=
|
|
15359
|
+
const cellOriginToCheck=messageParam.originatingCellOrigin;
|
|
15051
15360
|
|
|
15052
15361
|
// TRACE
|
|
15053
|
-
lognow(`INFO : Incoming node ${cellOriginToCheck} has said hello. Updating its local information...`);
|
|
15362
|
+
lognow(`INFO : (handleServerCellHelloSequence()) Incoming node ${cellOriginToCheck} has said hello. Updating its local information...`);
|
|
15054
15363
|
|
|
15055
15364
|
|
|
15056
15365
|
selfParam.incomingCells[cellOriginToCheck]={
|
|
15057
15366
|
connected:true,
|
|
15058
|
-
server:server,
|
|
15367
|
+
server:serverWrapper.server,
|
|
15059
15368
|
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
15060
15369
|
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
15061
15370
|
clientSocket:clientSocket,
|
|
15062
15371
|
};
|
|
15063
15372
|
|
|
15064
|
-
|
|
15373
|
+
|
|
15374
|
+
// NO SERVER CELL HELLO CONFIRMATION SENT BACK TO THE OTHER SERVER CELL (to save time)
|
|
15375
|
+
|
|
15376
|
+
},
|
|
15065
15377
|
listenerMessageType:"helloRequest"
|
|
15066
15378
|
};
|
|
15067
15379
|
|
|
@@ -15086,18 +15398,14 @@ class AORTACServerCell{
|
|
|
15086
15398
|
}
|
|
15087
15399
|
|
|
15088
15400
|
/*private*/handleCellIsReadySequence(){
|
|
15089
|
-
getOrCreateEmptyAttribute(
|
|
15090
|
-
this.onReceiveMessageListeners["both"]["protocol"],"cellIsReady")["forRequestsIssuedByOthers"]={
|
|
15091
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
15092
15401
|
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15402
|
+
this.onReceiveMessageListeners["both"]["protocol"]["cellIsReady"]={
|
|
15403
|
+
|
|
15404
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15405
|
+
|
|
15406
|
+
selfParam.cellsOverview[messageParam.originatingCellOrigin]={ready:true,startTime:messageParam.startTime};
|
|
15096
15407
|
|
|
15097
|
-
|
|
15098
|
-
// lognow("INFO : Updated cells overview for this cell ("+selfParam.selfOrigin+") :",selfParam.cellsOverview);
|
|
15099
|
-
// lognow("DEBUG : selfParam.outcomingCells :",Object.keys(selfParam.outcomingCells));
|
|
15100
|
-
// lognow("DEBUG : selfParam.incomingCells :",Object.keys(selfParam.incomingCells));
|
|
15408
|
+
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
15101
15409
|
|
|
15102
15410
|
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
15103
15411
|
selfParam.initiateQuorumIsReachedSequenceIfNecessary();
|
|
@@ -15123,11 +15431,11 @@ class AORTACServerCell{
|
|
|
15123
15431
|
const quorumCells=copy(this.cellsOverview);
|
|
15124
15432
|
|
|
15125
15433
|
// TRACE
|
|
15126
|
-
lognow("INFO : Quorum is reached and this is the latest started cell. quorumCells
|
|
15127
|
-
|
|
15128
|
-
this.
|
|
15129
|
-
|
|
15130
|
-
|
|
15434
|
+
lognow("INFO : Quorum is reached and this is the latest started cell (QLC). quorumCells:",quorumCells);
|
|
15435
|
+
|
|
15436
|
+
this.isServerCellQuorumLastCell=true;
|
|
15437
|
+
|
|
15438
|
+
this.launchModelPartitionBlobSequence(quorumCells);
|
|
15131
15439
|
|
|
15132
15440
|
}
|
|
15133
15441
|
|
|
@@ -15138,249 +15446,694 @@ class AORTACServerCell{
|
|
|
15138
15446
|
// ==========================================================
|
|
15139
15447
|
// MODEL MANAGEMENT
|
|
15140
15448
|
|
|
15141
|
-
/*private*/
|
|
15449
|
+
/*private*/async launchModelPartitionBlobSequence(quorumCells){
|
|
15142
15450
|
|
|
15143
15451
|
const self=this;
|
|
15144
15452
|
|
|
15145
|
-
|
|
15453
|
+
this.numberOfPartitions=getArraySize(quorumCells);
|
|
15454
|
+
|
|
15455
|
+
const controller=this.controller;
|
|
15456
|
+
// const model=this.model;
|
|
15457
|
+
|
|
15458
|
+
const partitionsInstantiationZones=controller.getPartitionsZones(this.numberOfPartitions);
|
|
15459
|
+
|
|
15460
|
+
const firstPartitionZone=getAt(partitionsInstantiationZones,0);
|
|
15461
|
+
|
|
15462
|
+
// We populate and initialize the server cell model, using the first partition zone :
|
|
15463
|
+
const model=await this.doOnModelZonePartitionReception(firstPartitionZone, this.selfOrigin);
|
|
15464
|
+
|
|
15465
|
+
// DBG
|
|
15466
|
+
lognow("DEBUG : MODEL WAS JUST CREATED.");
|
|
15467
|
+
|
|
15468
|
+
// We send the models partition zones objects to the required cells :
|
|
15469
|
+
const qlcRootContainer=self.partiallyPopulatedRootContainer();
|
|
15470
|
+
|
|
15471
|
+
let i=1;
|
|
15472
|
+
foreach(quorumCells, (quorumCell, quorumCellOrigin)=>{
|
|
15473
|
+
|
|
15474
|
+
const modelPartitionZone=getAt(partitionsInstantiationZones,i);
|
|
15475
|
+
|
|
15476
|
+
const message={qlcOrigin: self.selfOrigin, qlcRootContainer:qlcRootContainer, type:"modelPartitionZone", partitionZone:modelPartitionZone};
|
|
15477
|
+
|
|
15478
|
+
// TRACE
|
|
15479
|
+
lognow(`INFO : Server cell ${self.selfOrigin} is sending a partition zone to server cell ${quorumCellOrigin}...`, message);
|
|
15480
|
+
|
|
15481
|
+
self.sendMessageToBlob("protocol", message,
|
|
15482
|
+
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin],
|
|
15483
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
15484
|
+
isRequest:false});
|
|
15485
|
+
|
|
15486
|
+
i++;
|
|
15487
|
+
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
15488
|
+
|
|
15489
|
+
|
|
15490
|
+
// Each quorum member cell is responsible for a model partition
|
|
15491
|
+
// Then the sattelite cells will be handling the duplication
|
|
15492
|
+
|
|
15493
|
+
}
|
|
15494
|
+
|
|
15495
|
+
|
|
15496
|
+
// HANDLE PARTITION SEQUENCE
|
|
15497
|
+
|
|
15498
|
+
/*private*/handlePartitionSequence(){
|
|
15499
|
+
|
|
15500
|
+
// CAUTION : For *non-QLC* server cells only :
|
|
15501
|
+
|
|
15502
|
+
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
15503
|
+
this.onReceiveMessageListeners["both"]["protocol"]["modelPartitionZone"]={
|
|
15504
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15505
|
+
|
|
15506
|
+
// If this cell is in the destinations of this message, we pass it along and then we do what it says :
|
|
15507
|
+
selfParam.sendMessageToBlob("protocol", messageParam);
|
|
15508
|
+
|
|
15509
|
+
if(contains(messageParam.destinationCellsOrigins, selfParam.selfOrigin)){
|
|
15510
|
+
|
|
15511
|
+
// We populate and initialize the server cell model :
|
|
15512
|
+
selfParam.doOnModelZonePartitionReception(messageParam.partitionZone, messageParam.qlcOrigin, messageParam.qlcRootContainer).then(model=>{
|
|
15513
|
+
|
|
15514
|
+
});
|
|
15515
|
+
}
|
|
15516
|
+
|
|
15517
|
+
},
|
|
15518
|
+
listenerMessageType:"modelPartitionZone"
|
|
15519
|
+
};
|
|
15520
|
+
|
|
15521
|
+
}
|
|
15522
|
+
|
|
15523
|
+
|
|
15524
|
+
/*private*/doOnModelZonePartitionReception(partitionZone, qlcOrigin, qlcRootContainer=null){
|
|
15525
|
+
|
|
15526
|
+
return new Promise((resolve,reject)=>{
|
|
15527
|
+
|
|
15528
|
+
// TRACE
|
|
15529
|
+
lognow(`INFO : Applying partition zone for cell ${this.selfOrigin}. Updating local server model...`);
|
|
15530
|
+
lognow(`DEBUG : partitionZone:`, partitionZone);
|
|
15531
|
+
|
|
15532
|
+
this.partitionId=partitionZone.id;
|
|
15533
|
+
|
|
15534
|
+
// TRACE
|
|
15535
|
+
lognow("INFO : Populating model for this partition zone...");
|
|
15536
|
+
|
|
15537
|
+
if(this.isServerCellQuorumLastCell){ // Case QLC :
|
|
15538
|
+
|
|
15539
|
+
// DBG
|
|
15540
|
+
lognow("DEBUG : (this cell is the last quorum server cell (QLC)) ");
|
|
15541
|
+
|
|
15542
|
+
// We initialize the portion of the model :
|
|
15543
|
+
// (controller will handle if a persisted model already exists)
|
|
15544
|
+
// (in this particular case, qlcOrigin and this.selfOrigin is exactly the same !)
|
|
15545
|
+
this.controller.populateModelInZone(this.model, partitionZone, getHashedString(qlcOrigin), getHashedString(this.selfOrigin) ).then((model)=>{
|
|
15546
|
+
|
|
15547
|
+
// DBG
|
|
15548
|
+
lognow("DEBUG : model is populated in QLC server cell.");
|
|
15549
|
+
|
|
15550
|
+
resolve(model);
|
|
15551
|
+
});
|
|
15552
|
+
|
|
15553
|
+
}else{ // Case NON-QLC :
|
|
15554
|
+
|
|
15555
|
+
|
|
15556
|
+
// BASICALLY IT'S THE EXACT SAME TREATMENT AS FOR A BLOB CLIENT !
|
|
15557
|
+
|
|
15558
|
+
// DBG
|
|
15559
|
+
lognow("DEBUG : (this cell is *NOT* last quorum server cell (NON-QLC)) ");
|
|
15560
|
+
|
|
15561
|
+
|
|
15562
|
+
// We initialize the portion of the model :
|
|
15563
|
+
// (controller will handle if a persisted model already exists)
|
|
15564
|
+
this.controller.populateModelInZone(this.model, partitionZone, getHashedString(qlcOrigin), getHashedString(this.selfOrigin), qlcRootContainer ).then((model)=>{
|
|
15565
|
+
|
|
15566
|
+
// DBG
|
|
15567
|
+
lognow("DEBUG : model is populated in NON-QLC server cell.");
|
|
15568
|
+
|
|
15569
|
+
resolve(model);
|
|
15570
|
+
});
|
|
15571
|
+
|
|
15572
|
+
}
|
|
15573
|
+
|
|
15574
|
+
|
|
15575
|
+
});
|
|
15576
|
+
|
|
15577
|
+
}
|
|
15578
|
+
|
|
15579
|
+
|
|
15580
|
+
// ===================================================================================================
|
|
15581
|
+
// CLIENT
|
|
15582
|
+
// ===================================================================================================
|
|
15583
|
+
|
|
15584
|
+
// ==========================================================
|
|
15585
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
15586
|
+
|
|
15587
|
+
handleClientHelloSequence(){
|
|
15588
|
+
|
|
15589
|
+
// We wait to receive the hello request of the incoming client connection :
|
|
15590
|
+
this.onReceiveMessageListeners["incoming"]["protocol"]["clientHello"]={
|
|
15591
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15592
|
+
|
|
15593
|
+
const clientId=messageParam.clientId;
|
|
15594
|
+
const manifestationZone=messageParam.manifestationZone;
|
|
15595
|
+
|
|
15596
|
+
// TRACE
|
|
15597
|
+
lognow(`INFO : (handleClientHelloSequence()) Incoming client ${clientId} has said hello. Starting its reconnection sequence...`);
|
|
15598
|
+
|
|
15599
|
+
// We send the servers on which the client must reconnect to :
|
|
15600
|
+
|
|
15601
|
+
// Init lobule :
|
|
15602
|
+
selfParam.lobuleZone.clear(clientId);
|
|
15603
|
+
selfParam.lobuleZone.setClientConnectionInfo(clientId, serverWrapper, clientSocket);
|
|
15604
|
+
|
|
15605
|
+
// Caution : client must ignore this server cell if it has no objects for it !
|
|
15606
|
+
let allObjectsCorrespondingToZoneInServerCell=[];
|
|
15607
|
+
try{
|
|
15608
|
+
selfParam.model.checkAllMethodsCustomModelClassPrerequisites();
|
|
15609
|
+
allObjectsCorrespondingToZoneInServerCell=selfParam.model.getAllPartitionnableObjectsInZone(manifestationZone);
|
|
15610
|
+
}catch(e){
|
|
15611
|
+
//TRACE
|
|
15612
|
+
lognow(e);
|
|
15613
|
+
}
|
|
15614
|
+
|
|
15615
|
+
if(!empty(allObjectsCorrespondingToZoneInServerCell)){
|
|
15616
|
+
selfParam.lobuleZone.append(clientId, [selfParam.selfOrigin], "serversBag");
|
|
15617
|
+
selfParam.lobuleZone.append(clientId, allObjectsCorrespondingToZoneInServerCell);
|
|
15618
|
+
}else{
|
|
15619
|
+
// TRACE
|
|
15620
|
+
lognow("INFO : Server cell has no relevant objects to send to client");
|
|
15621
|
+
}
|
|
15622
|
+
|
|
15623
|
+
// The QLC in all cases, sends its structural objects to client :
|
|
15624
|
+
if(selfParam.isServerCellQuorumLastCell){
|
|
15625
|
+
// Case this server cell is the QLC :
|
|
15626
|
+
const rootContainer=selfParam.partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell);
|
|
15627
|
+
// Then we send the structure and, if any, the concerned objects in client manifestation zone of this server cell :
|
|
15628
|
+
selfParam.lobuleZone.append(clientId, [rootContainer]);
|
|
15629
|
+
}
|
|
15630
|
+
|
|
15631
|
+
// At this point , in the QLC server cell's lobule zone for this client we have :
|
|
15632
|
+
// (- maybe its concerned zone objects.)
|
|
15633
|
+
// - its root container
|
|
15634
|
+
|
|
15635
|
+
|
|
15636
|
+
// We ask the blob
|
|
15637
|
+
selfParam.sendMessageToBlob("protocol", {type:"getServersForZone", clientId:clientId, manifestationZone:manifestationZone},
|
|
15638
|
+
{isOriginatingCell:true,
|
|
15639
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
15640
|
+
isRequest:true,
|
|
15641
|
+
contactAllQuorumBlob:true})
|
|
15642
|
+
.thenOnAnyResponseFromBlobRequestingCellOnly((selfParam2, messageParamLocal)=>{
|
|
15643
|
+
// Triggered every time this server cell receives a response to its request from blob :
|
|
15644
|
+
|
|
15645
|
+
const clientIdLocal=messageParamLocal.clientId;
|
|
15646
|
+
const answeringServerOrigin=messageParamLocal.answeringServerOrigin;
|
|
15647
|
+
const objectsCount=messageParamLocal.objectsCount;
|
|
15648
|
+
|
|
15649
|
+
// DBG
|
|
15650
|
+
lognow("DEBUG : This server cell has received a response from another server cell : answeringServerOrigin:",answeringServerOrigin);
|
|
15651
|
+
|
|
15652
|
+
// When another server cells answers to this server cell :
|
|
15653
|
+
|
|
15654
|
+
if(!objectsCount || objectsCount<=0){
|
|
15655
|
+
// TRACE
|
|
15656
|
+
lognow("INFO : No objects corresponding to this zone to send to collector server cell. This server cell must be ignored.");
|
|
15657
|
+
}else{
|
|
15658
|
+
// We accumulate the server origin in the lobule :
|
|
15659
|
+
selfParam2.lobuleZone.append(clientIdLocal, [answeringServerOrigin], "serversBag");
|
|
15660
|
+
}
|
|
15661
|
+
|
|
15662
|
+
|
|
15663
|
+
}).doOnceAllBlobHasBeenContacted((selfParam2, messageParamLocal)=>{
|
|
15664
|
+
|
|
15665
|
+
// Triggered once the requesting server cell has received the response from the last possible other server cell, according to the partition ids !
|
|
15666
|
+
|
|
15667
|
+
const clientIdLocal=messageParamLocal.clientId;
|
|
15668
|
+
|
|
15669
|
+
// At this point we should have all the needed servers in the lobule :
|
|
15670
|
+
const servers=selfParam2.lobuleZone.getServers(clientIdLocal);
|
|
15671
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
15672
|
+
const objects=selfParam2.lobuleZone.getObjects(clientIdLocal);
|
|
15673
|
+
|
|
15674
|
+
// ???
|
|
15675
|
+
// if(!empty(objects)){
|
|
15676
|
+
// selfParam2.removeLinks(objects);
|
|
15677
|
+
// }
|
|
15678
|
+
|
|
15679
|
+
// CAUTION : THE QLC ALWAYS SENDS AT LEAST THE ROOTCONTAINER (during the «client hello» phase) !!!
|
|
15680
|
+
const messageWrapped=JSON.decycle({type:"clientHello.response", servers:servers, objects:objects});
|
|
15146
15681
|
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
controller.initializeModelForAORTACNode(model);
|
|
15154
|
-
|
|
15155
|
-
// This must return the asked number of partitions, indexed by partition id :
|
|
15156
|
-
// CAUTION : The partition function MUST return ALL the objects in a partition, WHATEVER THEIR NESTING LEVEL !!!
|
|
15157
|
-
const modelPartitions=model.getPartitions(numberOfPartitions);
|
|
15682
|
+
// DBG
|
|
15683
|
+
lognow("DEBUG : All blob has been contacted : messageWrapped:",messageWrapped);
|
|
15684
|
+
|
|
15685
|
+
// We send back all the collected servers to the requesting client :
|
|
15686
|
+
const clientConnectionInfo=selfParam2.lobuleZone.getClientConnectionInfo(clientIdLocal);
|
|
15687
|
+
clientConnectionInfo.serverWrapper.server.send("protocol", messageWrapped, null, clientConnectionInfo.clientSocket);
|
|
15158
15688
|
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
// We send the models objects to the required cells :
|
|
15164
|
-
let i=1;
|
|
15165
|
-
foreach(quorumCells, (quorumCell,quorumCellOrigin)=>{
|
|
15689
|
+
selfParam2.lobuleZone.clear(clientIdLocal);
|
|
15690
|
+
|
|
15691
|
+
});
|
|
15692
|
+
|
|
15166
15693
|
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
const message=JSON.decycle({type:"modelPartition",partition:modelPartition});
|
|
15171
|
-
|
|
15172
|
-
// TRACE
|
|
15173
|
-
lognow(`INFO : Cell ${self.selfOrigin} is sending a partition to cell ${quorumCellOrigin}...`,message);
|
|
15174
|
-
|
|
15175
|
-
self.sendMessageToBlob("protocol", message,
|
|
15176
|
-
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin], includeIncomingConnectionInTransmission:false, isRequest:false});
|
|
15694
|
+
},
|
|
15695
|
+
listenerMessageType:"clientHello"
|
|
15696
|
+
};
|
|
15177
15697
|
|
|
15178
|
-
i++;
|
|
15179
|
-
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
// Each quorum member cell is responsible for a model partition
|
|
15183
|
-
// Then the sattelite cells will be handling the duplication
|
|
15184
|
-
|
|
15185
15698
|
}
|
|
15186
|
-
|
|
15187
15699
|
|
|
15188
|
-
// HANDLE PARTITION SEQUENCE
|
|
15189
15700
|
|
|
15190
|
-
|
|
15701
|
+
// This is what other cells do when they receive a request for «getServersForZone» from a cell in the blob
|
|
15702
|
+
/*private*/handleGetServersForZoneRequest(){
|
|
15191
15703
|
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15704
|
+
this.onReceiveMessageListeners["both"]["protocol"]["getServersForZone"]={
|
|
15705
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15706
|
+
|
|
15707
|
+
// DBG
|
|
15708
|
+
lognow("DEBUG : handleGetServersForZoneRequest() : messageParam", messageParam);
|
|
15709
|
+
|
|
15710
|
+
const clientId=messageParam.clientId;
|
|
15711
|
+
const manifestationZone=messageParam.manifestationZone;
|
|
15712
|
+
|
|
15713
|
+
// When we receive a request from a server cell :
|
|
15714
|
+
let allObjectsCorrespondingToZoneInServerCell=[];
|
|
15715
|
+
try{
|
|
15716
|
+
selfParam.model.checkAllMethodsCustomModelClassPrerequisites();
|
|
15717
|
+
allObjectsCorrespondingToZoneInServerCell=selfParam.model.getAllPartitionnableObjectsInZone(manifestationZone);
|
|
15718
|
+
}catch(e){
|
|
15719
|
+
// TRACE
|
|
15720
|
+
lognow(e);
|
|
15721
|
+
}
|
|
15722
|
+
|
|
15723
|
+
// We store the objects for this client, because we know it will very soon ask them :
|
|
15724
|
+
// Init lobule :
|
|
15725
|
+
selfParam.lobuleZone.clear(clientId);
|
|
15726
|
+
selfParam.lobuleZone.setClientConnectionInfo(clientId, serverWrapper, clientSocket);
|
|
15727
|
+
|
|
15728
|
+
selfParam.lobuleZone.append(clientId, allObjectsCorrespondingToZoneInServerCell);
|
|
15729
|
+
|
|
15196
15730
|
|
|
15197
|
-
|
|
15198
|
-
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
15731
|
+
const message={type:"getServersForZone.response", objectsCount:allObjectsCorrespondingToZoneInServerCell.length, answeringServerOrigin:selfParam.selfOrigin};
|
|
15199
15732
|
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
const message=JSON.recycle(messageParam);
|
|
15203
|
-
|
|
15204
|
-
selfParam.doOnModelPartitionReception(message.partition);
|
|
15205
|
-
|
|
15206
|
-
}
|
|
15733
|
+
// TRACE
|
|
15734
|
+
lognow(`INFO : Server cell ${selfParam.selfOrigin} is sending its origin to server cell ${messageParam.originatingCellOrigin}...: message:`, message);
|
|
15207
15735
|
|
|
15208
|
-
|
|
15209
|
-
listenerMessageType:"
|
|
15736
|
+
selfParam.replyToBlobRequest("protocol", messageParam, message);
|
|
15737
|
+
},listenerMessageType:"getServersForZone"
|
|
15210
15738
|
};
|
|
15211
15739
|
|
|
15212
15740
|
}
|
|
15213
15741
|
|
|
15214
15742
|
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
lognow(`INFO : Incoming partition for cell ${this.selfOrigin}. Updating local model...`,partition);
|
|
15219
|
-
lognow(`>>>>`,stringifyObject(JSON.decycle(partition.objects),1));
|
|
15743
|
+
// ----
|
|
15744
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
15745
|
+
handleReconnectedClientHelloSequence(){
|
|
15220
15746
|
|
|
15747
|
+
// Here we only want to collect the servers on which to reconnect to later :
|
|
15221
15748
|
|
|
15749
|
+
// 2- We wait to receive the hello request of the incoming client connection :
|
|
15750
|
+
this.onReceiveMessageListeners["incoming"]["protocol"]["reconnectedClientHello"]={
|
|
15751
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15752
|
+
|
|
15753
|
+
const clientId=messageParam.clientId;
|
|
15754
|
+
|
|
15755
|
+
// TRACE
|
|
15756
|
+
lognow(`INFO : Incoming reconnected client ${clientId} has said hello. Updating its local information...`);
|
|
15757
|
+
|
|
15758
|
+
selfParam.clients[clientId]={
|
|
15759
|
+
connected:true,
|
|
15760
|
+
server:serverWrapper,
|
|
15761
|
+
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
15762
|
+
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
15763
|
+
clientSocket:clientSocket,
|
|
15764
|
+
};
|
|
15765
|
+
|
|
15766
|
+
const objects=selfParam.lobuleZone.getObjects(clientId);
|
|
15767
|
+
|
|
15768
|
+
// RECONNECTED CLIENT HELLO CONFIRMATION
|
|
15769
|
+
|
|
15770
|
+
|
|
15771
|
+
const messageResponse={type:"reconnectedClientHello.response", objects:objects};
|
|
15772
|
+
const messageResponseWrapped=JSON.decycle(messageResponse);
|
|
15773
|
+
|
|
15774
|
+
// DBG
|
|
15775
|
+
lognow("DEBUG : Sending «reconnectedClientHello.response»...");
|
|
15776
|
+
|
|
15777
|
+
serverWrapper.server.send("protocol", messageResponseWrapped, null, clientSocket);
|
|
15778
|
+
|
|
15779
|
+
selfParam.lobuleZone.clear(clientId);
|
|
15780
|
+
|
|
15781
|
+
},
|
|
15782
|
+
listenerMessageType:"reconnectedClientHello"
|
|
15783
|
+
};
|
|
15784
|
+
|
|
15222
15785
|
|
|
15223
15786
|
}
|
|
15224
15787
|
|
|
15225
15788
|
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
const self=this;
|
|
15229
|
-
|
|
15230
|
-
if(!currentObject){
|
|
15231
|
-
foreach(linkedObjects, obj=>{
|
|
15232
|
-
self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
15233
|
-
});
|
|
15234
|
-
}else{
|
|
15235
|
-
self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
15236
|
-
}
|
|
15237
|
-
|
|
15238
|
-
return linkedObjects;
|
|
15239
|
-
}
|
|
15789
|
+
// ==========================================================
|
|
15240
15790
|
|
|
15241
|
-
|
|
15791
|
+
|
|
15792
|
+
// ???
|
|
15793
|
+
// /*private*/removeLinks(linkedObjects, currentObject=null){
|
|
15794
|
+
// const self=this;
|
|
15795
|
+
// if(!currentObject){
|
|
15796
|
+
// foreach(linkedObjects, obj=>{
|
|
15797
|
+
// self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
15798
|
+
// });
|
|
15799
|
+
// }else{
|
|
15800
|
+
// self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
15801
|
+
// }
|
|
15802
|
+
// return linkedObjects;
|
|
15803
|
+
// }
|
|
15804
|
+
//
|
|
15805
|
+
// ???
|
|
15806
|
+
// /*private*/removeLinksOnSingleObject(linkedObjects, currentObject){
|
|
15807
|
+
// const self=this;
|
|
15808
|
+
// foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
15809
|
+
// // We only remove links to the objects not in the partition
|
|
15810
|
+
// if(isClassObject(attr)){
|
|
15811
|
+
// let aortacId=attr.aortacId;
|
|
15812
|
+
// if(!aortacId){
|
|
15813
|
+
// aortacId=getUUID();
|
|
15814
|
+
// attr.aortacId=aortacId;
|
|
15815
|
+
// }
|
|
15816
|
+
// if(!contains(linkedObjects,attr)){
|
|
15817
|
+
// currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
15818
|
+
// // CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
15819
|
+
// // WHATEVER THEIR NESTING LEVEL !!!
|
|
15820
|
+
// }
|
|
15821
|
+
// }else{
|
|
15822
|
+
// // However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
15823
|
+
// self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
15824
|
+
// }
|
|
15825
|
+
// },obj=>(isObject(obj) || isArray(obj)));
|
|
15826
|
+
// }
|
|
15242
15827
|
|
|
15243
|
-
const self=this;
|
|
15244
|
-
foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
15245
|
-
// We only remove links to the objects not in the partition
|
|
15246
|
-
if(isClassObject(attr)){
|
|
15247
|
-
let aortacId=attr.aortacId;
|
|
15248
|
-
if(!aortacId){
|
|
15249
|
-
aortacId=getUUID();
|
|
15250
|
-
attr.aortacId=aortacId;
|
|
15251
|
-
}
|
|
15252
|
-
if(!contains(linkedObjects,attr)){
|
|
15253
|
-
currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
15254
|
-
// CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
15255
|
-
// WHATEVER THEIR NESTING LEVEL !!!
|
|
15256
|
-
}
|
|
15257
|
-
}else{
|
|
15258
|
-
// However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
15259
|
-
self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
15260
|
-
}
|
|
15261
|
-
},obj=>(isObject(obj) || isArray(obj)));
|
|
15262
15828
|
|
|
15263
|
-
}
|
|
15264
15829
|
|
|
15265
15830
|
|
|
15266
|
-
//
|
|
15267
|
-
|
|
15268
|
-
/*private*/
|
|
15831
|
+
// **********************************************************************
|
|
15832
|
+
|
|
15833
|
+
/*private*/partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell=[]){
|
|
15269
15834
|
|
|
15270
|
-
|
|
15271
|
-
lognow("DEBUG : collectDependencies()...",inputObjects);
|
|
15835
|
+
const rootContainerLocal=this.model.getRootContainer();
|
|
15272
15836
|
|
|
15837
|
+
const rootContainer=this.model.populateRootContainerWithObjectsSubset(rootContainerLocal, allObjectsCorrespondingToZoneInServerCell, true);
|
|
15838
|
+
rootContainer.isRootContainer=true;
|
|
15273
15839
|
|
|
15840
|
+
return rootContainer;
|
|
15274
15841
|
}
|
|
15275
|
-
|
|
15276
|
-
/*private*/repercutChangesIfNeeded(inputObjects){
|
|
15277
|
-
|
|
15278
|
-
|
|
15279
|
-
////
|
|
15280
|
-
lognow("DEBUG : repercutChangesIfNeeded()...",inputObjects);
|
|
15281
15842
|
|
|
15282
|
-
|
|
15283
|
-
}
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
15843
|
}
|
|
15287
15844
|
|
|
15288
15845
|
|
|
15289
15846
|
|
|
15290
|
-
// ******************************************************************
|
|
15291
15847
|
|
|
15292
|
-
// Public static hydration method :
|
|
15293
|
-
window.ao=(incompleteModelObjectToDecorate)=>{
|
|
15294
|
-
|
|
15295
|
-
const localServerCell=window.aortacCServerNodeInstance;
|
|
15296
|
-
if(!localServerCell){
|
|
15297
|
-
// TRACE
|
|
15298
|
-
lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
15299
|
-
return incompleteModelObjectToDecorate;
|
|
15300
|
-
}
|
|
15301
|
-
|
|
15302
|
-
const liveModelObjects=localServerCell.liveModelObjects;
|
|
15303
|
-
|
|
15304
|
-
// First we clone the object, for its attriutes values information :
|
|
15305
|
-
const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
15306
|
-
|
|
15307
|
-
// Then we replace all its methods :
|
|
15308
|
-
foreach(clonedObject, (method, methodName)=>{
|
|
15309
|
-
clonedObject[methodName]=new Proxy(method,
|
|
15310
|
-
{
|
|
15311
|
-
apply: function(methodToEnhance, thisArg, argumentsList) {
|
|
15312
15848
|
|
|
15313
|
-
const inputObjects=[thisArg];
|
|
15314
|
-
inputObjects.push(...argumentsList);
|
|
15315
|
-
localServerCell.collectDependencies(inputObjects);
|
|
15316
|
-
|
|
15317
|
-
// // --- Treatment BEFORE function execution ---
|
|
15318
|
-
// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
15319
|
-
|
|
15320
|
-
// Call the original function (target) with its intended 'this' context (thisArg)
|
|
15321
|
-
// and arguments (argumentsList) using Reflect.apply
|
|
15322
|
-
const result = Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
15323
|
-
|
|
15324
|
-
// // --- Treatment AFTER function execution ---
|
|
15325
|
-
// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
15326
|
-
|
|
15327
|
-
inputObjects.push(result);
|
|
15328
|
-
localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
15329
15849
|
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
},attribute=>isFunction(attribute));
|
|
15850
|
+
|
|
15851
|
+
|
|
15852
|
+
|
|
15853
|
+
class ServerCellLobule{
|
|
15335
15854
|
|
|
15336
|
-
|
|
15337
|
-
|
|
15855
|
+
constructor(){
|
|
15856
|
+
this.serversBag=null;
|
|
15857
|
+
this.objectsBag=null;
|
|
15858
|
+
this.clear();
|
|
15859
|
+
this.clientsConnectionsInfos={};
|
|
15860
|
+
}
|
|
15861
|
+
append(clientId, objs, attributeName="objectsBag"){
|
|
15862
|
+
const self=this;
|
|
15863
|
+
if(!this[attributeName][clientId])
|
|
15864
|
+
this[attributeName][clientId]=[];
|
|
15865
|
+
foreach(objs,obj=>{
|
|
15866
|
+
self[attributeName][clientId].push(obj);
|
|
15867
|
+
},(obj)=>(!contains(this[attributeName][clientId], obj)));
|
|
15868
|
+
}
|
|
15869
|
+
getObjects(clientId, filterFunction=null){
|
|
15870
|
+
if(!filterFunction) return this.objectsBag[clientId];
|
|
15871
|
+
if(!this.objectsBag[clientId]) return null;
|
|
15872
|
+
const results=[];
|
|
15873
|
+
foreach(this.objectsBag[clientId], (obj)=>{
|
|
15874
|
+
results.push(obj);
|
|
15875
|
+
},filterFunction);
|
|
15876
|
+
return results;
|
|
15877
|
+
}
|
|
15878
|
+
getServers(clientId){
|
|
15879
|
+
if(!this.serversBag[clientId]) return null;
|
|
15880
|
+
return this.serversBag[clientId];
|
|
15881
|
+
}
|
|
15882
|
+
clear(clientId=null, attributeName=null){
|
|
15883
|
+
if(!attributeName){
|
|
15884
|
+
if(!clientId){
|
|
15885
|
+
this.serversBag={};
|
|
15886
|
+
this.objectsBag={};
|
|
15887
|
+
}else{
|
|
15888
|
+
this.serversBag[clientId]=[];
|
|
15889
|
+
this.objectsBag[clientId]=[];
|
|
15890
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
15891
|
+
}
|
|
15892
|
+
}else{
|
|
15893
|
+
if(!clientId){
|
|
15894
|
+
this[attributeName]={};
|
|
15895
|
+
}else{
|
|
15896
|
+
this[attributeName][clientId]=[];
|
|
15897
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
15898
|
+
}
|
|
15899
|
+
}
|
|
15900
|
+
|
|
15901
|
+
//DBG
|
|
15902
|
+
if(clientId) lognow("DEBUG : CLEAR() FOR clientId:",clientId);
|
|
15903
|
+
|
|
15904
|
+
return this;
|
|
15905
|
+
}
|
|
15906
|
+
remove(clientId){
|
|
15907
|
+
delete this.serversBag[clientId];
|
|
15908
|
+
delete this.objectsBag[clientId];
|
|
15909
|
+
delete this.clientsConnectionsInfos[clientId];
|
|
15910
|
+
}
|
|
15911
|
+
getClientConnectionInfo(clientId){
|
|
15912
|
+
return this.clientsConnectionsInfos[clientId];
|
|
15913
|
+
}
|
|
15914
|
+
setClientConnectionInfo(clientId, serverWrapper, clientSocket){
|
|
15915
|
+
this.clientsConnectionsInfos[clientId]={serverWrapper:serverWrapper, clientSocket:clientSocket};
|
|
15916
|
+
}
|
|
15917
|
+
}
|
|
15338
15918
|
|
|
15339
15919
|
|
|
15340
15920
|
|
|
15341
|
-
window.
|
|
15921
|
+
window.getAORTACServerCell=function(quorumNumber=1, selfOrigin="ws://127.0.0.1:40000", outcomingCellsOrigins=[], model, controller){
|
|
15342
15922
|
//return new AORTACServerNode("node_"+getUUID("short"), selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15343
|
-
if(window.
|
|
15923
|
+
if(window.aortacCServerCellInstance){
|
|
15344
15924
|
// TRACE
|
|
15345
|
-
throw new Error("ERROR : The
|
|
15925
|
+
throw new Error("ERROR : The aortacCServerCellInstance singleton instance already exists. It cannot be instantiated again in the same process. Aborting.");
|
|
15346
15926
|
}
|
|
15347
|
-
window.
|
|
15348
|
-
return window.
|
|
15927
|
+
window.aortacCServerCellInstance=new AORTACServerCell(quorumNumber, selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15928
|
+
return window.aortacCServerCellInstance;
|
|
15349
15929
|
}
|
|
15350
15930
|
|
|
15351
15931
|
|
|
15352
15932
|
|
|
15353
15933
|
|
|
15934
|
+
// ===============================================================================================================================
|
|
15935
|
+
// ===============================================================================================================================
|
|
15936
|
+
// UTILITY METHODS
|
|
15354
15937
|
|
|
15355
15938
|
|
|
15939
|
+
// USAGE EXAMPLE :
|
|
15356
15940
|
|
|
15357
|
-
// ==================================================================================================================
|
|
15358
15941
|
|
|
15942
|
+
// 4 tests only :
|
|
15943
|
+
//class PricingService {
|
|
15944
|
+
// constructor(currency) {
|
|
15945
|
+
// this.currency = currency;
|
|
15946
|
+
// this.callCount = 0;
|
|
15947
|
+
// }
|
|
15948
|
+
//
|
|
15949
|
+
// getPrice(eventId) {
|
|
15950
|
+
// this.callCount++;
|
|
15951
|
+
// return { eventId, amount: 89.99, currency: this.currency };
|
|
15952
|
+
// }
|
|
15953
|
+
//
|
|
15954
|
+
// async updatePrice(eventId, amount) {
|
|
15955
|
+
// await new Promise(r => setTimeout(r, 10)); // simulate async
|
|
15956
|
+
// return { eventId, amount, saved: true };
|
|
15957
|
+
// }
|
|
15958
|
+
//}
|
|
15959
|
+
//
|
|
15960
|
+
//proxyClass(PricingService);
|
|
15961
|
+
//const proxiedClassInstance=new PricingService("CAD");
|
|
15962
|
+
//proxiedClassInstance.currency; // [GET] PricingService.currency{ value: "CAD" }
|
|
15963
|
+
//proxiedClassInstance.currency="USD"; // [SET] PricingService.currency{ value: "USD" }
|
|
15964
|
+
//proxiedClassInstance.getPrice("evt-42"); // [CALL] PricingService.getPrice(){ args: ["evt-42"], result:{...} }
|
|
15965
|
+
//proxiedClassInstance.callCount; // [GET] PricingService.callCount{ value: 1 }
|
|
15966
|
+
//await proxiedClassInstance.updatePrice("evt-42", 120); // [CALL] PricingService.updatePrice(){ args: [...], result:{...} }
|
|
15967
|
+
//restoreNonProxiedClass(PricingService); // undo — new PricingService() works normally again
|
|
15359
15968
|
|
|
15360
|
-
// AORTAC CLIENT
|
|
15361
15969
|
|
|
15970
|
+
window.proxyClass=function(classToProxy,
|
|
15971
|
+
doOnAccess=(className, propertyName, parameter)=>{lognow(`Accessing ${className}.${propertyName}:`,parameter);}){
|
|
15362
15972
|
|
|
15973
|
+
const className=classToProxy.name;
|
|
15363
15974
|
|
|
15364
|
-
|
|
15975
|
+
// Build the Proxy handler
|
|
15976
|
+
const proxyHandler={
|
|
15365
15977
|
|
|
15366
|
-
//
|
|
15978
|
+
// Intercepts: obj.property AND obj.method()
|
|
15979
|
+
get(concernedObject, property, thisArg){
|
|
15980
|
+
const value=Reflect.get(concernedObject, property, thisArg);
|
|
15367
15981
|
|
|
15368
|
-
|
|
15982
|
+
// We skip symbols (ex. Symbol.toPrimitive) — not meaningful to intercept
|
|
15983
|
+
if(typeof(property)!=="string") return value;
|
|
15984
|
+
|
|
15985
|
+
if(typeof(value)==="function"){
|
|
15986
|
+
|
|
15987
|
+
// Return a wrapper so we can intercept args + return value at call time
|
|
15988
|
+
return function(...args){
|
|
15989
|
+
const result=value.apply(concernedObject, args); // `concernedObject` keeps correct `this`
|
|
15990
|
+
|
|
15991
|
+
if(result instanceof Promise){
|
|
15992
|
+
// Async method — intercept after the promise settles
|
|
15993
|
+
return result
|
|
15994
|
+
.then(v=>{ doOnAccess(className, property, { args, result: v }); return v; })
|
|
15995
|
+
.catch(e=>{ doOnAccess(className, property, { args, error: e }); throw e; });
|
|
15996
|
+
}
|
|
15997
|
+
|
|
15998
|
+
// Sync method — intercept immediately
|
|
15999
|
+
doOnAccess(className, property, { args, result }/*(SYNTAX : Equivalent to { args:args, result:result })*/);
|
|
16000
|
+
return result;
|
|
16001
|
+
};
|
|
16002
|
+
}
|
|
16003
|
+
|
|
16004
|
+
// Plain attribute read
|
|
16005
|
+
doOnAccess(className, property, { value });
|
|
16006
|
+
return value;
|
|
16007
|
+
},
|
|
16008
|
+
|
|
16009
|
+
// Intercepts: obj.property=value
|
|
16010
|
+
set(concernedObject, property, valueToSet, thisArg){
|
|
16011
|
+
if(typeof(property)==="string")
|
|
16012
|
+
doOnAccess(className, property, { valueToSet });
|
|
16013
|
+
return Reflect.set(concernedObject, property, valueToSet, thisArg);
|
|
16014
|
+
},
|
|
16015
|
+
};
|
|
16016
|
+
|
|
16017
|
+
// Patch the constructor
|
|
16018
|
+
// Save the original so we can restore later
|
|
16019
|
+
const originalConstructor=classToProxy.prototype.constructor;
|
|
16020
|
+
classToProxy.prototype.originalConstructor=originalConstructor;
|
|
16021
|
+
|
|
16022
|
+
// Override the constructor: it runs first, then we wrap `this` in a Proxy
|
|
16023
|
+
classToProxy.prototype.constructor=function(...args){
|
|
16024
|
+
classToProxy.prototype.originalConstructor.apply(this, args); // run original setup
|
|
16025
|
+
return new Proxy(this, proxyHandler); // return proxy instead of raw `this`
|
|
16026
|
+
};
|
|
16027
|
+
|
|
16028
|
+
// Keep the name and prototype intact so instanceof still works
|
|
16029
|
+
Object.defineProperty(classToProxy.prototype.constructor, "name", { value: className });
|
|
16030
|
+
classToProxy.prototype.constructor.prototype=classToProxy.prototype;
|
|
16031
|
+
|
|
16032
|
+
return classToProxy;
|
|
16033
|
+
};
|
|
16034
|
+
|
|
16035
|
+
// Return a restore function
|
|
16036
|
+
window.restoreNonProxiedClass=function(classToProxy){
|
|
16037
|
+
if(!classToProxy.prototype.originalConstructor){
|
|
16038
|
+
// TRACE
|
|
16039
|
+
lognow("WARN : Class has not been proxied. We do nothing. Concerned class:",classToProxy);
|
|
16040
|
+
return;
|
|
16041
|
+
}
|
|
16042
|
+
classToProxy.prototype.constructor=classToProxy.prototype.originalConstructor;
|
|
16043
|
+
};
|
|
16044
|
+
|
|
16045
|
+
|
|
16046
|
+
|
|
16047
|
+
|
|
16048
|
+
// ******************************************************************
|
|
16049
|
+
|
|
16050
|
+
window.getCurrentLevelConfigForAORTACServerCell=()=>{
|
|
16051
|
+
const allPrototypesConfig=PROTOTYPES_CONFIG.allPrototypes;
|
|
16052
|
+
const levelConfig=allPrototypesConfig.GameLevel;
|
|
16053
|
+
|
|
16054
|
+
const currentLevelPrototypeName=getCurrentLevelPrototypeNameForAORTACServerCell();
|
|
16055
|
+
if(nothing(currentLevelPrototypeName)){
|
|
16056
|
+
// TRACE
|
|
16057
|
+
lognow("WARN : No currentLevelPrototypeName found. Aborting.");
|
|
16058
|
+
return null;
|
|
16059
|
+
}
|
|
16060
|
+
const referenceCurrentLevelConfig=levelConfig[currentLevelPrototypeName];
|
|
16061
|
+
return referenceCurrentLevelConfig;
|
|
16062
|
+
};
|
|
16063
|
+
|
|
16064
|
+
window.getCurrentLevelPrototypeNameForAORTACServerCell=()=>{
|
|
15369
16065
|
|
|
15370
|
-
|
|
16066
|
+
const aortacConfig=GAME_CONFIG.aortac;
|
|
16067
|
+
if(nothing(aortacConfig)){
|
|
16068
|
+
// TRACE
|
|
16069
|
+
lognow("WARN : No AORTAC config, Aborting..");
|
|
16070
|
+
return null;
|
|
16071
|
+
}
|
|
15371
16072
|
|
|
16073
|
+
const allPrototypesConfig=PROTOTYPES_CONFIG.allPrototypes;
|
|
16074
|
+
const levelConfig=allPrototypesConfig.GameLevel;
|
|
16075
|
+
|
|
16076
|
+
let currentLevelPrototypeName=aortacConfig.startingLevelPrototypeName;
|
|
16077
|
+
if(nothing(currentLevelPrototypeName)){
|
|
16078
|
+
currentLevelPrototypeName=getKeyAt(levelConfig,0);
|
|
16079
|
+
// TRACE
|
|
16080
|
+
lognow("WARN : No startingLevelPrototypeName specified in AORTAC config, using first level name as default «"+currentLevelPrototypeName+"».");
|
|
15372
16081
|
}
|
|
15373
16082
|
|
|
15374
|
-
|
|
16083
|
+
return currentLevelPrototypeName;
|
|
16084
|
+
};
|
|
15375
16085
|
|
|
15376
16086
|
|
|
15377
16087
|
|
|
15378
16088
|
|
|
15379
|
-
window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
15380
|
-
//return new AORTACClient("client_"+getUUID(), serverNodeOrigin, model, view, isNodeContext);
|
|
15381
|
-
return new AORTACClientCell(serverNodeOrigin, model, view, isNodeContext);
|
|
15382
|
-
}
|
|
15383
16089
|
|
|
16090
|
+
//
|
|
16091
|
+
//// Public static hydration method :
|
|
16092
|
+
//window.ao=(incompleteModelObjectToDecorate)=>{
|
|
16093
|
+
//
|
|
16094
|
+
// const localServerCell=window.aortacCServerCellInstance;
|
|
16095
|
+
// if(!localServerCell){
|
|
16096
|
+
// // TRACE
|
|
16097
|
+
// lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
16098
|
+
// return incompleteModelObjectToDecorate;
|
|
16099
|
+
// }
|
|
16100
|
+
//
|
|
16101
|
+
// const liveModelObjects=localServerCell.liveModelObjects;
|
|
16102
|
+
//
|
|
16103
|
+
// // First we clone the object, for its attriutes values information :
|
|
16104
|
+
// const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
16105
|
+
//
|
|
16106
|
+
// // Then we replace all its methods :
|
|
16107
|
+
// foreach(clonedObject, (method, methodName)=>{
|
|
16108
|
+
// clonedObject[methodName]=new Proxy(method,
|
|
16109
|
+
// {
|
|
16110
|
+
// apply: function(methodToEnhance, thisArg, argumentsList){
|
|
16111
|
+
//
|
|
16112
|
+
// const inputObjects=[thisArg];
|
|
16113
|
+
// inputObjects.push(...argumentsList);
|
|
16114
|
+
// localServerCell.collectDependencies(inputObjects);
|
|
16115
|
+
//
|
|
16116
|
+
//// // --- Treatment BEFORE function execution ---
|
|
16117
|
+
//// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
16118
|
+
//
|
|
16119
|
+
// // Call the original function(target) with its intended "this" context (thisArg)
|
|
16120
|
+
// // and arguments (argumentsList) using Reflect.apply
|
|
16121
|
+
// const result=Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
16122
|
+
//
|
|
16123
|
+
//// // --- Treatment AFTER function execution ---
|
|
16124
|
+
//// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
16125
|
+
//
|
|
16126
|
+
// inputObjects.push(result);
|
|
16127
|
+
// localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
16128
|
+
//
|
|
16129
|
+
// return result;
|
|
16130
|
+
// },
|
|
16131
|
+
// }
|
|
16132
|
+
// );
|
|
16133
|
+
// },attribute=>isFunction(attribute));
|
|
16134
|
+
//
|
|
16135
|
+
// return clonedObject;
|
|
16136
|
+
//};
|
|
15384
16137
|
|
|
15385
16138
|
|
|
15386
16139
|
|
|
@@ -16096,7 +16849,7 @@ WebsocketImplementation={
|
|
|
16096
16849
|
},
|
|
16097
16850
|
|
|
16098
16851
|
|
|
16099
|
-
/*
|
|
16852
|
+
/*public*//*static*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
|
|
16100
16853
|
|
|
16101
16854
|
const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
|
|
16102
16855
|
|
|
@@ -16293,12 +17046,11 @@ WebsocketImplementation={
|
|
|
16293
17046
|
|
|
16294
17047
|
|
|
16295
17048
|
|
|
16296
|
-
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
|
|
17049
|
+
launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, doOnConnectionLost=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
|
|
16297
17050
|
|
|
16298
17051
|
const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
|
|
16299
17052
|
|
|
16300
17053
|
|
|
16301
|
-
|
|
16302
17054
|
if(typeof(https)==="undefined"){
|
|
16303
17055
|
// TRACE
|
|
16304
17056
|
console.log("«https» SERVER library not called yet, calling it now.");
|
|
@@ -16390,8 +17142,6 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16390
17142
|
|
|
16391
17143
|
const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
|
|
16392
17144
|
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
17145
|
let listenableServer;
|
|
16396
17146
|
if(sslOptions){
|
|
16397
17147
|
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
@@ -16412,7 +17162,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16412
17162
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
16413
17163
|
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
16414
17164
|
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
16415
|
-
});
|
|
17165
|
+
}, doOnConnectionLost);
|
|
16416
17166
|
|
|
16417
17167
|
|
|
16418
17168
|
server.onFinalize((serverParam)=>{
|
|
@@ -16427,15 +17177,13 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16427
17177
|
// TRACE
|
|
16428
17178
|
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
16429
17179
|
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
17180
|
|
|
16433
17181
|
|
|
16434
17182
|
return server;
|
|
16435
17183
|
}
|
|
16436
17184
|
|
|
16437
17185
|
|
|
16438
|
-
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
|
|
17186
|
+
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, doOnConnectionLost=null,
|
|
16439
17187
|
/*OPTIONAL*/portParam,
|
|
16440
17188
|
/*OPTIONAL*/certPathParam,
|
|
16441
17189
|
/*OPTIONAL*/keyPathParam){
|
|
@@ -16488,13 +17236,11 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16488
17236
|
|
|
16489
17237
|
|
|
16490
17238
|
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
17239
|
const aotraNodeServer={config:serverConfig};
|
|
16494
17240
|
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
16495
17241
|
|
|
16496
17242
|
if(isHashAsked){
|
|
16497
|
-
// We
|
|
17243
|
+
// We instantiate a temporary persister just to read the key hash file:
|
|
16498
17244
|
const persister=getPersister("./");
|
|
16499
17245
|
let persisterIdSplits=persisterId.split("@");
|
|
16500
17246
|
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
@@ -16577,9 +17323,9 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16577
17323
|
}
|
|
16578
17324
|
}
|
|
16579
17325
|
|
|
16580
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
17326
|
+
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, doOnConnectionLost, sslOptions);
|
|
17327
|
+
|
|
16581
17328
|
|
|
16582
|
-
|
|
16583
17329
|
return aotraNodeServer;
|
|
16584
17330
|
};
|
|
16585
17331
|
|
|
@@ -16642,7 +17388,10 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16642
17388
|
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
16643
17389
|
//
|
|
16644
17390
|
//
|
|
16645
|
-
// },
|
|
17391
|
+
// },
|
|
17392
|
+
// // On client connection lost :
|
|
17393
|
+
// (clientId)=>{lognow(`INFO : Connection to client id «${clientId}» was lost.`);},
|
|
17394
|
+
// portParam,certPathParam,keyPathParam);
|
|
16646
17395
|
//
|
|
16647
17396
|
//
|
|
16648
17397
|
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
@@ -17110,8 +17859,8 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
17110
17859
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
17111
17860
|
socketToServerClientInstance.onConnectionToServer(()=>{
|
|
17112
17861
|
if(doOnServerConnection){
|
|
17113
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance]);
|
|
17114
|
-
else doOnServerConnection(socketToServerClientInstance);
|
|
17862
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance, aotraClient]);
|
|
17863
|
+
else doOnServerConnection(socketToServerClientInstance, aotraClient);
|
|
17115
17864
|
}
|
|
17116
17865
|
|
|
17117
17866
|
|
|
@@ -17208,8 +17957,9 @@ class ClientReceptionEntryPoint{
|
|
|
17208
17957
|
|
|
17209
17958
|
|
|
17210
17959
|
// We check if the message matches the required message type :
|
|
17211
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17212
|
-
&&
|
|
17960
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17961
|
+
&& dataWrapped.data
|
|
17962
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
17213
17963
|
return;
|
|
17214
17964
|
}
|
|
17215
17965
|
|
|
@@ -17233,8 +17983,12 @@ class ClientReceptionEntryPoint{
|
|
|
17233
17983
|
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
17234
17984
|
remove(clientReceptionEntryPoints, this);
|
|
17235
17985
|
|
|
17236
|
-
if(this.doOnIncomingMessage)
|
|
17237
|
-
|
|
17986
|
+
if(this.doOnIncomingMessage){
|
|
17987
|
+
|
|
17988
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
17989
|
+
|
|
17990
|
+
this.doOnIncomingMessage(dataRestored, clientSocket);
|
|
17991
|
+
}
|
|
17238
17992
|
|
|
17239
17993
|
}
|
|
17240
17994
|
|
|
@@ -17277,7 +18031,9 @@ class ClientInstance{
|
|
|
17277
18031
|
// I-
|
|
17278
18032
|
//
|
|
17279
18033
|
self.receive(channelNameForResponse, doOnIncomingMessageForResponse,
|
|
17280
|
-
|
|
18034
|
+
{messageId:messageId, destroyListenerAfterReceiving:true},
|
|
18035
|
+
clientsRoomsTag, listenerId,
|
|
18036
|
+
);
|
|
17281
18037
|
//
|
|
17282
18038
|
|
|
17283
18039
|
return resultPromise;
|
|
@@ -17292,7 +18048,7 @@ class ClientInstance{
|
|
|
17292
18048
|
}
|
|
17293
18049
|
|
|
17294
18050
|
// II-
|
|
17295
|
-
receive(channelNameParam, doOnIncomingMessage,
|
|
18051
|
+
receive(channelNameParam, doOnIncomingMessage, listenerConfig={destroyListenerAfterReceiving:false, listenerMessageType:null}, clientsRoomsTag=null, entryPointId=null){
|
|
17296
18052
|
const self=this;
|
|
17297
18053
|
|
|
17298
18054
|
// // DBG
|
|
@@ -17332,8 +18088,11 @@ class ClientInstance{
|
|
|
17332
18088
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17333
18089
|
|
|
17334
18090
|
// Channel information is stored in exchanged data :
|
|
17335
|
-
let dataWrapped={channelName:channelNameParam, data:data};
|
|
17336
|
-
|
|
18091
|
+
let dataWrapped=saveClassesInformation({channelName:channelNameParam, data:data});
|
|
18092
|
+
|
|
18093
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
18094
|
+
// (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)
|
|
18095
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
17337
18096
|
dataWrapped=stringifyObject(dataWrapped);
|
|
17338
18097
|
|
|
17339
18098
|
// TODO : FIXME : Use one single interface !
|
|
@@ -17465,8 +18224,9 @@ class ServerReceptionEntryPoint{
|
|
|
17465
18224
|
if(!isClientInRoom) return;
|
|
17466
18225
|
|
|
17467
18226
|
// We check if the message matches the required message type :
|
|
17468
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17469
|
-
|
|
18227
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
18228
|
+
&& dataWrapped.data
|
|
18229
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
17470
18230
|
return;
|
|
17471
18231
|
}
|
|
17472
18232
|
|
|
@@ -17474,7 +18234,11 @@ class ServerReceptionEntryPoint{
|
|
|
17474
18234
|
// // DBG
|
|
17475
18235
|
// lognow("(SERVER) this.doOnIncomingMessage:");
|
|
17476
18236
|
|
|
17477
|
-
|
|
18237
|
+
const dataLocal=dataWrapped.data;
|
|
18238
|
+
|
|
18239
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
18240
|
+
|
|
18241
|
+
this.doOnIncomingMessage(dataRestored, clientSocketParam);
|
|
17478
18242
|
}
|
|
17479
18243
|
|
|
17480
18244
|
}
|
|
@@ -17522,13 +18286,10 @@ class NodeServerInstance{
|
|
|
17522
18286
|
});
|
|
17523
18287
|
}
|
|
17524
18288
|
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
18289
|
// TODO : DEVELOP...
|
|
17528
18290
|
//sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
17529
18291
|
//}
|
|
17530
18292
|
|
|
17531
|
-
|
|
17532
18293
|
receive(channelNameParam, doOnIncomingMessage, listenerConfig=null, clientsRoomsTag=null){
|
|
17533
18294
|
|
|
17534
18295
|
// DBG
|
|
@@ -17576,14 +18337,16 @@ class NodeServerInstance{
|
|
|
17576
18337
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17577
18338
|
|
|
17578
18339
|
// Channel information is stored in exchanged data :
|
|
17579
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
17580
|
-
|
|
17581
|
-
|
|
18340
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
18341
|
+
|
|
18342
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
18343
|
+
// (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)
|
|
18344
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
17582
18345
|
dataWrapped=stringifyObject(dataWrapped);
|
|
17583
18346
|
|
|
17584
18347
|
// TODO : FIXME : Use one single interface !
|
|
17585
18348
|
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
17586
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
18349
|
+
else clientSocket.emit(channelName, dataWrapped);
|
|
17587
18350
|
|
|
17588
18351
|
});
|
|
17589
18352
|
|
|
@@ -17603,12 +18366,12 @@ class NodeServerInstance{
|
|
|
17603
18366
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17604
18367
|
|
|
17605
18368
|
// Channel information is stored in exchanged data :
|
|
17606
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
17607
|
-
dataWrapped=stringifyObject(dataWrapped);
|
|
18369
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
17608
18370
|
|
|
17609
|
-
|
|
17610
|
-
//
|
|
17611
|
-
|
|
18371
|
+
// CAUTION : WE CAN ONLY SEND STRINGS !!!
|
|
18372
|
+
// (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)
|
|
18373
|
+
// (Don't worry, the underlying sub-system will turn it back into Objects without you need to do anything!)
|
|
18374
|
+
dataWrapped=stringifyObject(dataWrapped);
|
|
17612
18375
|
|
|
17613
18376
|
|
|
17614
18377
|
// TODO : FIXME : Use one single interface !
|
|
@@ -17621,8 +18384,8 @@ class NodeServerInstance{
|
|
|
17621
18384
|
return this;
|
|
17622
18385
|
}
|
|
17623
18386
|
|
|
17624
|
-
|
|
17625
|
-
onConnectionToClient(doOnConnection){
|
|
18387
|
+
|
|
18388
|
+
onConnectionToClient(doOnConnection, doOnConnectionLost=null){
|
|
17626
18389
|
const self=this;
|
|
17627
18390
|
|
|
17628
18391
|
|
|
@@ -17631,7 +18394,6 @@ class NodeServerInstance{
|
|
|
17631
18394
|
|
|
17632
18395
|
// DBG
|
|
17633
18396
|
console.log("SERVER : ON CONNECTION !");
|
|
17634
|
-
|
|
17635
18397
|
|
|
17636
18398
|
const clientId="autogeneratedid_"+getUUID();
|
|
17637
18399
|
|
|
@@ -17682,6 +18444,9 @@ class NodeServerInstance{
|
|
|
17682
18444
|
|
|
17683
18445
|
|
|
17684
18446
|
clearInterval(clientSocket.stateCheckInterval);
|
|
18447
|
+
|
|
18448
|
+
if(doOnConnectionLost) doOnConnectionLost(clientSocket.clientId);
|
|
18449
|
+
|
|
17685
18450
|
return;
|
|
17686
18451
|
}
|
|
17687
18452
|
|
|
@@ -17740,7 +18505,71 @@ class NodeServerInstance{
|
|
|
17740
18505
|
|
|
17741
18506
|
|
|
17742
18507
|
|
|
18508
|
+
window.saveClassesInformation=(obj, visitedObjects=[])=>{
|
|
18509
|
+
|
|
18510
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
18511
|
+
|
|
18512
|
+
if(contains(visitedObjects, obj)) return obj;
|
|
18513
|
+
visitedObjects.push(obj);
|
|
18514
|
+
|
|
18515
|
+
const className=getClassName(obj);
|
|
18516
|
+
if(className!="Object"){
|
|
18517
|
+
// We do the save :
|
|
18518
|
+
obj[attributeClassName]=className;
|
|
18519
|
+
|
|
18520
|
+
// DBG
|
|
18521
|
+
lognow("0 saving class name : :",obj);
|
|
18522
|
+
|
|
18523
|
+
}
|
|
18524
|
+
|
|
18525
|
+
foreach(obj, (attr, attrName)=>{
|
|
18526
|
+
const className=getClassName(attr);
|
|
18527
|
+
|
|
18528
|
+
// DBG
|
|
18529
|
+
if(attrName=="position") lognow("1 attr:",attr);
|
|
18530
|
+
|
|
18531
|
+
if(className!="Object"){
|
|
18532
|
+
saveClassesInformation(attr, visitedObjects);
|
|
18533
|
+
}
|
|
18534
|
+
|
|
18535
|
+
// DBG
|
|
18536
|
+
if(attrName=="position") lognow("2 attr:",attr);
|
|
18537
|
+
|
|
18538
|
+
},(attr, attrName)=>
|
|
18539
|
+
attr &&
|
|
18540
|
+
// DEBUG ONLY
|
|
18541
|
+
(attrName=="position" || (
|
|
18542
|
+
isObject(attr) && !attr[attributeClassName]
|
|
18543
|
+
))
|
|
18544
|
+
);
|
|
18545
|
+
|
|
18546
|
+
return obj;
|
|
18547
|
+
};
|
|
18548
|
+
|
|
18549
|
+
|
|
18550
|
+
window.restoreClassesInformation=(obj, visitedObjects=[])=>{
|
|
18551
|
+
|
|
18552
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
18553
|
+
|
|
18554
|
+
if(contains(visitedObjects, obj)) return obj;
|
|
18555
|
+
visitedObjects.push(obj);
|
|
18556
|
+
|
|
18557
|
+
const className=obj[attributeClassName];
|
|
18558
|
+
if(className){
|
|
18559
|
+
// We do the restore :
|
|
18560
|
+
const blankInstance=instantiate(className);
|
|
18561
|
+
obj=Object.assign(blankInstance, obj);
|
|
18562
|
+
}
|
|
18563
|
+
|
|
18564
|
+
foreach(obj, (attr, attrName)=>{
|
|
18565
|
+
const className=attr[attributeClassName];
|
|
18566
|
+
if(className!="Object"){
|
|
18567
|
+
obj[attrName]=restoreClassesInformation(attr, visitedObjects);
|
|
18568
|
+
}
|
|
18569
|
+
},attr=>attr && ((isObject(attr) && attr[attributeClassName]) || isArray(attr)) );
|
|
17743
18570
|
|
|
18571
|
+
return obj;
|
|
18572
|
+
};
|
|
17744
18573
|
|
|
17745
18574
|
|
|
17746
18575
|
|