aotrautils 0.0.1842 → 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 +1537 -715
- 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,20 +4152,26 @@ window.instanciate=function(className=null){
|
|
|
4130
4152
|
classInWindow=global[className];
|
|
4131
4153
|
if(!classInWindow){
|
|
4132
4154
|
// TRACE
|
|
4133
|
-
lognow(`ERROR : CAUTION : «${className}» class does not seem to be registered in the global object. Cannot
|
|
4155
|
+
lognow(`ERROR : CAUTION : «${className}» class does not seem to be registered in the global object. Cannot instantiate it.`);
|
|
4134
4156
|
|
|
4157
|
+
// DEBUG
|
|
4135
4158
|
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4136
4159
|
newObj=eval("new "+className+"();");
|
|
4137
4160
|
|
|
4138
4161
|
}
|
|
4139
4162
|
}else{
|
|
4140
|
-
//
|
|
4141
|
-
lognow(`ERROR : CAUTION : «${className}» class does not seem to be accessible in the globalThis nor window object, and global object does not exist. Cannot
|
|
4163
|
+
// TRACEfireNewPuff()
|
|
4164
|
+
lognow(`ERROR : CAUTION : «${className}» class does not seem to be accessible in the globalThis nor window object, and global object does not exist. Cannot instantiate it.`);
|
|
4142
4165
|
|
|
4166
|
+
// DEBUG
|
|
4143
4167
|
// TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4144
4168
|
newObj=eval("new "+className+"();");
|
|
4145
4169
|
|
|
4146
4170
|
}
|
|
4171
|
+
|
|
4172
|
+
// WORKAROUND
|
|
4173
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4174
|
+
|
|
4147
4175
|
return newObj;
|
|
4148
4176
|
}
|
|
4149
4177
|
}
|
|
@@ -4154,21 +4182,21 @@ window.instanciate=function(className=null){
|
|
|
4154
4182
|
// newObj=Reflect.construct(classInWindow);
|
|
4155
4183
|
// }catch(e1){
|
|
4156
4184
|
//// // TRACE
|
|
4157
|
-
//// console.log("WARN : Could not
|
|
4185
|
+
//// console.log("WARN : Could not instantiate class «"+className+"». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4158
4186
|
// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4159
4187
|
// newObj=eval("new "+className+"();");
|
|
4160
4188
|
// }
|
|
4161
4189
|
// }catch(e2){
|
|
4162
4190
|
//// // TRACE
|
|
4163
|
-
//// console.log("ERROR : Could not
|
|
4191
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with eval. (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4164
4192
|
// try{
|
|
4165
4193
|
//// // TODO : FIXME : I don't like that at all, to use eval(...), but on today I know of no other solution... :
|
|
4166
4194
|
//// newObj=eval("new window."+className+"();");
|
|
4167
4195
|
// newObj=Reflect.construct(classInWindow);
|
|
4168
4196
|
// }catch(e3){
|
|
4169
4197
|
//// // TRACE
|
|
4170
|
-
//// console.log("ERROR : Could not
|
|
4171
|
-
//// console.log("ERROR : Could not
|
|
4198
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with eval and «window.». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4199
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with Reflect and «window.». (Maybe class is undefined or default constructor doesn't exist !)");
|
|
4172
4200
|
// // TODO : FIXME : This is the most performance-costing fallback :
|
|
4173
4201
|
// try{
|
|
4174
4202
|
// if(classInWindow){
|
|
@@ -4180,7 +4208,7 @@ window.instanciate=function(className=null){
|
|
|
4180
4208
|
// }
|
|
4181
4209
|
// }catch(e4){
|
|
4182
4210
|
//// // TRACE
|
|
4183
|
-
//// console.log("ERROR : Could not
|
|
4211
|
+
//// console.log("ERROR : Could not instantiate class «"+className+"» with prototype affectation.");
|
|
4184
4212
|
//// console.log("ERROR : Returning plain object since all instantiation methods have failed for class «"+className+"».");
|
|
4185
4213
|
// }
|
|
4186
4214
|
// }
|
|
@@ -4188,17 +4216,17 @@ window.instanciate=function(className=null){
|
|
|
4188
4216
|
|
|
4189
4217
|
// NEW :
|
|
4190
4218
|
newObj=new classInWindow();
|
|
4219
|
+
|
|
4220
|
+
// WORKAROUND
|
|
4221
|
+
newObj[CLASSNAME_ATTRIBUTE_NAME]=className;
|
|
4222
|
+
|
|
4191
4223
|
return newObj;
|
|
4192
4224
|
};
|
|
4193
4225
|
|
|
4194
4226
|
|
|
4195
4227
|
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
4228
|
JSON.stringifyDecycle=function(obj){
|
|
4201
|
-
let decycled=JSON.decycle(obj,null,
|
|
4229
|
+
let decycled=JSON.decycle(obj,null,CLASSNAME_ATTRIBUTE_NAME);
|
|
4202
4230
|
// CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
|
|
4203
4231
|
return stringifyObject(decycled);
|
|
4204
4232
|
}
|
|
@@ -4206,6 +4234,8 @@ JSON.stringifyDecycle=function(obj){
|
|
|
4206
4234
|
|
|
4207
4235
|
JSON.parseRecycle=function(str){
|
|
4208
4236
|
|
|
4237
|
+
const attributeClassName=CLASSNAME_ATTRIBUTE_NAME;
|
|
4238
|
+
|
|
4209
4239
|
const parsedDecycled=parseJSON(str);
|
|
4210
4240
|
let restoreClass=function(objParam){
|
|
4211
4241
|
|
|
@@ -4215,11 +4245,11 @@ JSON.parseRecycle=function(str){
|
|
|
4215
4245
|
if(isArray(objParam)){
|
|
4216
4246
|
newObj=[];
|
|
4217
4247
|
}else{
|
|
4218
|
-
let className=objParam[
|
|
4248
|
+
let className=objParam[attributeClassName];
|
|
4219
4249
|
if(!className || isNativeClassName(className)){
|
|
4220
4250
|
newObj= {};
|
|
4221
4251
|
}else{
|
|
4222
|
-
newObj=
|
|
4252
|
+
newObj=instantiate(className);
|
|
4223
4253
|
}
|
|
4224
4254
|
}
|
|
4225
4255
|
|
|
@@ -4249,8 +4279,11 @@ JSON.parseRecycle=function(str){
|
|
|
4249
4279
|
,(itemOrAttr)=>{ return !isFunction(itemOrAttr); }
|
|
4250
4280
|
);
|
|
4251
4281
|
|
|
4252
|
-
|
|
4253
|
-
|
|
4282
|
+
// However we have to keep the class information attribute for further house !
|
|
4283
|
+
if(destination[attributeClassName]
|
|
4284
|
+
//...except if the object is a simple object :
|
|
4285
|
+
&& destination[attributeClassName]=="Object"){
|
|
4286
|
+
delete destination[attributeClassName];
|
|
4254
4287
|
}
|
|
4255
4288
|
|
|
4256
4289
|
};
|
|
@@ -4268,6 +4301,7 @@ JSON.parseRecycle=function(str){
|
|
|
4268
4301
|
|
|
4269
4302
|
|
|
4270
4303
|
window.clone=function(obj){
|
|
4304
|
+
if(obj==null) return null;
|
|
4271
4305
|
// OLD :
|
|
4272
4306
|
const str=JSON.stringifyDecycle(obj);
|
|
4273
4307
|
const newObj=JSON.parseRecycle(str);
|
|
@@ -4740,7 +4774,7 @@ window.isFlatMap=function(obj){
|
|
|
4740
4774
|
|
|
4741
4775
|
window.getAsFlatStructure=function(rawObject
|
|
4742
4776
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4743
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4777
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4744
4778
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4745
4779
|
){
|
|
4746
4780
|
// Flattening :
|
|
@@ -4791,7 +4825,7 @@ function flattenGraph(root, UUID_ATTR_NAME, CLASSNAME_ATTR_NAME, POINTER_TO_ATTR
|
|
|
4791
4825
|
|
|
4792
4826
|
window.getAsTreeStructure=function(oldMap, keepClassName=false
|
|
4793
4827
|
,UUID_ATTR_NAME=DEFAULT_UUID_ATTR_NAME/* Yet Another UUID */
|
|
4794
|
-
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another
|
|
4828
|
+
,CLASSNAME_ATTR_NAME=DEFAULT_CLASSNAME_ATTR_NAME/* Yet Another ClassName */
|
|
4795
4829
|
,POINTER_TO_ATTR_NAME=DEFAULT_POINTER_TO_ATTR_NAME/* Yet Another PointerTo */
|
|
4796
4830
|
){
|
|
4797
4831
|
|
|
@@ -4813,7 +4847,7 @@ window.restoreGraph=(flatData, keepClassName=false, UUID_ATTR_NAME, CLASSNAME_AT
|
|
|
4813
4847
|
|
|
4814
4848
|
const objData = flatData[id];
|
|
4815
4849
|
const className = objData[CLASSNAME_ATTR_NAME];
|
|
4816
|
-
const newInstance =
|
|
4850
|
+
const newInstance = instantiate(className);
|
|
4817
4851
|
|
|
4818
4852
|
restoredObjects[id]=newInstance; // Assign ID early to handle circular references
|
|
4819
4853
|
|
|
@@ -5178,7 +5212,7 @@ window.getMonoThreadedTimeout=function(actuator=null){
|
|
|
5178
5212
|
|
|
5179
5213
|
// ======================== Routine ========================
|
|
5180
5214
|
|
|
5181
|
-
window.MonoThreadedRoutine=class {
|
|
5215
|
+
window.MonoThreadedRoutine=class MonoThreadedRoutine{
|
|
5182
5216
|
constructor(actuator,refreshMillis=null,startDependsOnParentOnly=false){
|
|
5183
5217
|
|
|
5184
5218
|
this.actuator=actuator;
|
|
@@ -5188,15 +5222,21 @@ window.MonoThreadedRoutine=class {
|
|
|
5188
5222
|
this.time=getNow();
|
|
5189
5223
|
this.refreshMillis=refreshMillis;
|
|
5190
5224
|
|
|
5191
|
-
this.
|
|
5225
|
+
this.managedTimeFactor=1;
|
|
5192
5226
|
// Three-states : started, paused, stopped.
|
|
5193
5227
|
|
|
5194
5228
|
}
|
|
5195
5229
|
|
|
5196
|
-
|
|
5197
|
-
this
|
|
5230
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5231
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5232
|
+
return this;
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5235
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5236
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5198
5237
|
return this;
|
|
5199
5238
|
}
|
|
5239
|
+
|
|
5200
5240
|
isStarted(){
|
|
5201
5241
|
return this.started || this.startDependsOnParentOnly;
|
|
5202
5242
|
}
|
|
@@ -5233,7 +5273,7 @@ window.MonoThreadedRoutine=class {
|
|
|
5233
5273
|
if(!this.isStarted() || this.paused) return;
|
|
5234
5274
|
// Looping index with a delay :
|
|
5235
5275
|
|
|
5236
|
-
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5276
|
+
const delayHasPassed=hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor);
|
|
5237
5277
|
if(!this.refreshMillis || delayHasPassed){
|
|
5238
5278
|
if(this.refreshMillis && delayHasPassed){
|
|
5239
5279
|
this.time=getNow();
|
|
@@ -5243,7 +5283,8 @@ window.MonoThreadedRoutine=class {
|
|
|
5243
5283
|
this.stop(args);
|
|
5244
5284
|
return;
|
|
5245
5285
|
}
|
|
5246
|
-
if(this.actuator.doOnEachStep)
|
|
5286
|
+
if(this.actuator.doOnEachStep)
|
|
5287
|
+
this.actuator.doOnEachStep(args);
|
|
5247
5288
|
}
|
|
5248
5289
|
|
|
5249
5290
|
}
|
|
@@ -5272,19 +5313,27 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5272
5313
|
this.time=getNow();
|
|
5273
5314
|
this.refreshMillis=refreshMillis;
|
|
5274
5315
|
|
|
5275
|
-
this.
|
|
5316
|
+
this.managedTimeFactor=1;
|
|
5317
|
+
|
|
5276
5318
|
|
|
5277
5319
|
// Three-states : started, paused, stopped.
|
|
5278
5320
|
}
|
|
5279
|
-
|
|
5280
|
-
|
|
5321
|
+
|
|
5322
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
5323
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
5324
|
+
return this;
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
5328
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
5281
5329
|
|
|
5282
5330
|
// We recalculate total time-dependent values :
|
|
5283
|
-
this.totalTimeMillis=this.totalTimeMillis*this.
|
|
5331
|
+
this.totalTimeMillis=this.totalTimeMillis*this.managedTimeFactor;
|
|
5284
5332
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.actualValue))/this.totalTimeMillis);
|
|
5285
5333
|
|
|
5286
5334
|
return this;
|
|
5287
5335
|
}
|
|
5336
|
+
|
|
5288
5337
|
isStarted(){
|
|
5289
5338
|
return this.started || this.startDependsOnParentOnly;
|
|
5290
5339
|
}
|
|
@@ -5296,7 +5345,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5296
5345
|
setNewGoal(goalValue, refreshMillisParam=null, totalTimeMillis=null){
|
|
5297
5346
|
if(Math.round(this.value)===Math.round(goalValue)) return;
|
|
5298
5347
|
if(refreshMillisParam) this.refreshMillis=refreshMillisParam;
|
|
5299
|
-
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.
|
|
5348
|
+
if(totalTimeMillis) this.totalTimeMillis=totalTimeMillis*this.managedTimeFactor;
|
|
5300
5349
|
|
|
5301
5350
|
this.goalValue=goalValue;
|
|
5302
5351
|
this.stepValue=((this.refreshMillis*(this.goalValue-this.value))/this.totalTimeMillis);
|
|
@@ -5383,7 +5432,7 @@ window.MonoThreadedGoToGoal=class {
|
|
|
5383
5432
|
}
|
|
5384
5433
|
|
|
5385
5434
|
hasDelayPassed(){
|
|
5386
|
-
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.
|
|
5435
|
+
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor));
|
|
5387
5436
|
}
|
|
5388
5437
|
|
|
5389
5438
|
|
|
@@ -5409,7 +5458,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
|
5409
5458
|
|
|
5410
5459
|
|
|
5411
5460
|
|
|
5412
|
-
/*utils 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)»*/
|
|
5413
5462
|
/*-----------------------------------------------------------------------------*/
|
|
5414
5463
|
/* ## Utility global methods in a browser (htmljs) client environment.
|
|
5415
5464
|
*
|
|
@@ -6124,7 +6173,7 @@ function getTextWordsExtract(textStrParam, wordsNumber,/* OPTIONAL */wordPositio
|
|
|
6124
6173
|
|
|
6125
6174
|
//==================== CLIENT Geometry ====================
|
|
6126
6175
|
|
|
6127
|
-
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){
|
|
6128
6177
|
|
|
6129
6178
|
if(center==null) center={x:"center",y:"center"};
|
|
6130
6179
|
|
|
@@ -6148,8 +6197,6 @@ Math.isInScreen=function(coords,size=null,center=null,totalWidth,totalHeight,cam
|
|
|
6148
6197
|
}
|
|
6149
6198
|
|
|
6150
6199
|
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
6200
|
let isInScreenX=false;
|
|
6154
6201
|
{
|
|
6155
6202
|
let min;
|
|
@@ -8328,7 +8375,7 @@ function filterPoints(allPoints ,ctx/*DBG*/){
|
|
|
8328
8375
|
|
|
8329
8376
|
// This temporary, off-screen video is always needed to get the image data for further use :
|
|
8330
8377
|
videoTag=document.createElement("video");
|
|
8331
|
-
videoTag.id="tmpVideoElement";
|
|
8378
|
+
videoTag.id="tmpVideoElement"+getUUID("short");
|
|
8332
8379
|
videoTag.autoplay=true;
|
|
8333
8380
|
videoTag.width=canvasTag.width;
|
|
8334
8381
|
videoTag.height=canvasTag.height;
|
|
@@ -8668,12 +8715,6 @@ drawVideoImage=function(canvas,videoImage,
|
|
|
8668
8715
|
var x=nonull(xParam,0);
|
|
8669
8716
|
var y=nonull(yParam,0);
|
|
8670
8717
|
|
|
8671
|
-
// // DOES NOT WORK WELL :
|
|
8672
|
-
// if(Object.getPrototypeOf(videoImage).constructor.name==="VideoFrame"){
|
|
8673
|
-
// ctx.drawImage(videoImage, x, y, newWidthParam, newHeightParam);
|
|
8674
|
-
// return;
|
|
8675
|
-
// }
|
|
8676
|
-
|
|
8677
8718
|
var oldWidth=videoImage.width;
|
|
8678
8719
|
var oldHeight=videoImage.height;
|
|
8679
8720
|
|
|
@@ -9507,14 +9548,18 @@ class SpriteMonoThreaded{
|
|
|
9507
9548
|
// Time measurement :
|
|
9508
9549
|
this.time=getNow();
|
|
9509
9550
|
this.refreshMillis=refreshMillis;
|
|
9510
|
-
this.durationTimeFactorHolder={durationTimeFactor:1};
|
|
9511
9551
|
|
|
9552
|
+
this.managedTimeFactor=1;
|
|
9512
9553
|
// Two-states : started, stopped.
|
|
9513
|
-
|
|
9554
|
+
}
|
|
9555
|
+
|
|
9556
|
+
registerToTimeFactorManager(timeFactorManager){
|
|
9557
|
+
timeFactorManager.registerTimeFactorManagee(this);
|
|
9558
|
+
return this;
|
|
9514
9559
|
}
|
|
9515
9560
|
|
|
9516
|
-
|
|
9517
|
-
this.
|
|
9561
|
+
changeManagedTimeFactor(newManagedTimeFactor){
|
|
9562
|
+
this.managedTimeFactor=newManagedTimeFactor;
|
|
9518
9563
|
return this;
|
|
9519
9564
|
}
|
|
9520
9565
|
|
|
@@ -9584,7 +9629,7 @@ class SpriteMonoThreaded{
|
|
|
9584
9629
|
}
|
|
9585
9630
|
|
|
9586
9631
|
hasDelayPassed(){
|
|
9587
|
-
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.
|
|
9632
|
+
return (!this.time || hasDelayPassed(this.time, this.refreshMillis*this.managedTimeFactor));
|
|
9588
9633
|
}
|
|
9589
9634
|
|
|
9590
9635
|
}
|
|
@@ -13235,7 +13280,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
|
|
|
13235
13280
|
|
|
13236
13281
|
|
|
13237
13282
|
|
|
13238
|
-
/*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)»*/
|
|
13239
13284
|
/*-----------------------------------------------------------------------------*/
|
|
13240
13285
|
|
|
13241
13286
|
|
|
@@ -13990,171 +14035,6 @@ window.getSunShadowProjectedPoint=function(originalPoint,angleDegreesParam,groun
|
|
|
13990
14035
|
}
|
|
13991
14036
|
|
|
13992
14037
|
|
|
13993
|
-
window.isInZone=function(point, zone, center={x:"center",y:"center",z:"center"}, zoneOffsets=null, invertYAxis=false, zooms=null){
|
|
13994
|
-
|
|
13995
|
-
if(!center){
|
|
13996
|
-
center={x:"center",y:"center",z:"center"}
|
|
13997
|
-
}
|
|
13998
|
-
if(invertYAxis==null){
|
|
13999
|
-
invertYAxis=false;
|
|
14000
|
-
}
|
|
14001
|
-
if(zooms){
|
|
14002
|
-
point=getZoomedPosition(point, zooms);
|
|
14003
|
-
zone=getZoomedZone(zone, zooms);
|
|
14004
|
-
}
|
|
14005
|
-
|
|
14006
|
-
let pointX=point.x;
|
|
14007
|
-
let pointY=point.y;
|
|
14008
|
-
let pointZ=point.z;
|
|
14009
|
-
|
|
14010
|
-
let zoneX=zone.x;
|
|
14011
|
-
let zoneY=zone.y;
|
|
14012
|
-
let zoneZ=zone.z;
|
|
14013
|
-
if(zoneOffsets){
|
|
14014
|
-
if(zoneX!=null) zoneX+=nonull(zoneOffsets.x,0);
|
|
14015
|
-
if(zoneY!=null) zoneY+=nonull(zoneOffsets.y,0);
|
|
14016
|
-
if(zoneZ!=null) zoneZ+=nonull(zoneOffsets.z,0);
|
|
14017
|
-
}
|
|
14018
|
-
|
|
14019
|
-
// We ignore zone parameters if they are null or 0 :
|
|
14020
|
-
let isXInZone=zone.w?false:true;
|
|
14021
|
-
let isYInZone=zone.h?false:true;
|
|
14022
|
-
let isZInZone=zone.d?false:true;
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
if(zone.w || zone.h || zone.d){
|
|
14026
|
-
|
|
14027
|
-
if(zone.w){
|
|
14028
|
-
if(pointX!=null && zoneX!=null){
|
|
14029
|
-
if(center.x==="center"){
|
|
14030
|
-
let demiSize=Math.floor(zone.w*.5);
|
|
14031
|
-
if((zoneX - demiSize) <= pointX && pointX <= (zoneX + demiSize) )
|
|
14032
|
-
isXInZone=true;
|
|
14033
|
-
}else if(center.x==="right"){
|
|
14034
|
-
if((zoneX - zone.w) <= pointX && pointX <= zoneX )
|
|
14035
|
-
isXInZone=true;
|
|
14036
|
-
}else if(center.x==="left"){
|
|
14037
|
-
if(zoneX <= pointX && pointX <= (zoneX + zone.w) )
|
|
14038
|
-
isXInZone=true;
|
|
14039
|
-
}
|
|
14040
|
-
}else{
|
|
14041
|
-
isXInZone=true;
|
|
14042
|
-
}
|
|
14043
|
-
}
|
|
14044
|
-
|
|
14045
|
-
if(zone.h){
|
|
14046
|
-
if(pointY!=null && zoneY!=null){
|
|
14047
|
-
if(center.y==="center"){
|
|
14048
|
-
let demiSize=Math.floor(zone.h*.5);
|
|
14049
|
-
if((zoneY - demiSize) <= pointY && pointY <= (zoneY + demiSize) )
|
|
14050
|
-
isYInZone=true;
|
|
14051
|
-
}else if(center.y==="top"){
|
|
14052
|
-
if(!invertYAxis){
|
|
14053
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14054
|
-
isYInZone=true;
|
|
14055
|
-
}else{
|
|
14056
|
-
if(zoneY <= pointY && pointY <= (zoneY + zone.h) )
|
|
14057
|
-
isYInZone=true;
|
|
14058
|
-
}
|
|
14059
|
-
}else if(center.y==="bottom"){
|
|
14060
|
-
if(!invertYAxis){
|
|
14061
|
-
if(zoneY <= pointY && pointY <= (zoneY + zone.h) )
|
|
14062
|
-
isYInZone=true;
|
|
14063
|
-
}else{
|
|
14064
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14065
|
-
isYInZone=true;
|
|
14066
|
-
}
|
|
14067
|
-
}
|
|
14068
|
-
}else{
|
|
14069
|
-
isYInZone=true;
|
|
14070
|
-
}
|
|
14071
|
-
}
|
|
14072
|
-
|
|
14073
|
-
if(zone.d){
|
|
14074
|
-
if(pointZ!=null && zoneZ!=null){
|
|
14075
|
-
if(center.z==="center"){
|
|
14076
|
-
let demiSize=Math.floor(zone.d*.5);
|
|
14077
|
-
if((zoneZ - demiSize) <= pointZ && pointZ <= (zoneZ + demiSize) )
|
|
14078
|
-
isZInZone=true;
|
|
14079
|
-
}else if(center.z==="front"){
|
|
14080
|
-
if((zoneZ - zone.d) <= pointZ && pointZ <= zoneZ )
|
|
14081
|
-
isZInZone=true;
|
|
14082
|
-
}else if(center.z==="back"){
|
|
14083
|
-
if(zoneZ <= pointZ && pointZ <= (zoneZ + zone.d) )
|
|
14084
|
-
isZInZone=true;
|
|
14085
|
-
}
|
|
14086
|
-
}else{
|
|
14087
|
-
isZInZone=true;
|
|
14088
|
-
}
|
|
14089
|
-
}
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
}else{
|
|
14093
|
-
|
|
14094
|
-
if(pointX!=null && zoneX!=null){
|
|
14095
|
-
if(center.x==="center"){
|
|
14096
|
-
let demiW=Math.floor(zone.w*.5);
|
|
14097
|
-
if((zoneX - demiW) <= pointX && pointX <= (zoneX + demiW))
|
|
14098
|
-
isXInZone=true;
|
|
14099
|
-
}else if(center.x==="right"){
|
|
14100
|
-
if((zoneX - zone.w) <= pointX && pointX <= zoneX )
|
|
14101
|
-
isXInZone=true;
|
|
14102
|
-
}else if(center.x==="left"){
|
|
14103
|
-
if( zoneX <= pointX && pointX <= (zoneX + zone.w ) )
|
|
14104
|
-
isXInZone=true;
|
|
14105
|
-
}
|
|
14106
|
-
}else{
|
|
14107
|
-
isXInZone=true;
|
|
14108
|
-
}
|
|
14109
|
-
|
|
14110
|
-
if(pointY!=null && zoneY!=null){
|
|
14111
|
-
if(center.y==="center"){
|
|
14112
|
-
let demiH=Math.floor(zone.h*.5);
|
|
14113
|
-
if((zoneY - demiH) <= pointY && pointY <= (zoneY + demiH))
|
|
14114
|
-
isYInZone=true;
|
|
14115
|
-
}else if(center.y==="top"){
|
|
14116
|
-
if(!invertYAxis){
|
|
14117
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14118
|
-
isYInZone=true;
|
|
14119
|
-
}else{
|
|
14120
|
-
if( zoneY <= pointY && pointY <= (zoneY + zone.h ) )
|
|
14121
|
-
isYInZone=true;
|
|
14122
|
-
}
|
|
14123
|
-
}else if(center.y==="bottom"){
|
|
14124
|
-
if(!invertYAxis){
|
|
14125
|
-
if( zoneY <= pointY && pointY <= (zoneY + zone.h ) )
|
|
14126
|
-
isYInZone=true;
|
|
14127
|
-
}else{
|
|
14128
|
-
if((zoneY - zone.h) <= pointY && pointY <= zoneY )
|
|
14129
|
-
isYInZone=true;
|
|
14130
|
-
}
|
|
14131
|
-
}
|
|
14132
|
-
}else{
|
|
14133
|
-
isYInZone=true;
|
|
14134
|
-
}
|
|
14135
|
-
|
|
14136
|
-
if(pointZ!=null && zoneZ!=null){
|
|
14137
|
-
if(center.z==="center"){
|
|
14138
|
-
let demiW=Math.floor(zone.d*.5);
|
|
14139
|
-
if((zoneZ - demiW) <= pointZ && pointZ <= (zoneZ + demiW))
|
|
14140
|
-
isZInZone=true;
|
|
14141
|
-
}else if(center.z==="front"){
|
|
14142
|
-
if((zoneZ - zone.d) <= pointZ && pointZ <= zoneZ )
|
|
14143
|
-
isZInZone=true;
|
|
14144
|
-
}else if(center.z==="back"){
|
|
14145
|
-
if( zoneZ <= pointZ && pointZ <= (zoneZ + zone.d ) )
|
|
14146
|
-
isZInZone=true;
|
|
14147
|
-
}
|
|
14148
|
-
}else{
|
|
14149
|
-
isZInZone=true;
|
|
14150
|
-
}
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
}
|
|
14154
|
-
|
|
14155
|
-
return isXInZone && isYInZone && isZInZone;
|
|
14156
|
-
}
|
|
14157
|
-
|
|
14158
14038
|
|
|
14159
14039
|
Math.getQuadrant=function(centerPoint,point){
|
|
14160
14040
|
let deltaX=point.x-centerPoint.x;
|
|
@@ -14291,85 +14171,6 @@ Math.getBidimensional8Direction=(currentPosition,destination,invertYAxis=false)=
|
|
|
14291
14171
|
|
|
14292
14172
|
|
|
14293
14173
|
|
|
14294
|
-
/*public static*/window.getRandomPositionInZone=function(positionZone, xOffset=0, yOffset=0, zOffset=0, avoidOverlap=null, size=null, randomizeAngles=false){
|
|
14295
|
-
|
|
14296
|
-
let w=nonull(positionZone.w,0);
|
|
14297
|
-
let h=nonull(positionZone.h,0);
|
|
14298
|
-
let d=nonull(positionZone.d,0);
|
|
14299
|
-
|
|
14300
|
-
let demiW=w*.5;
|
|
14301
|
-
let demiH=h*.5;
|
|
14302
|
-
let demiD=d*.5;
|
|
14303
|
-
|
|
14304
|
-
let xOrZero=nonull(positionZone.x,0);
|
|
14305
|
-
let yOrZero=nonull(positionZone.y,0);
|
|
14306
|
-
let zOrZero=nonull(positionZone.z,0);
|
|
14307
|
-
|
|
14308
|
-
// If respectively width or height is 0, then there is no need to use a randomization !
|
|
14309
|
-
let x= ( w<=0 ? xOrZero : (xOrZero + Math.getRandomInt(Math.floor(demiW), -Math.ceil(demiW)))) + xOffset;
|
|
14310
|
-
let y= ( h<=0 ? yOrZero : (yOrZero + Math.getRandomInt(Math.floor(demiH), -Math.ceil(demiH)))) + yOffset;
|
|
14311
|
-
let z= ( d<=0 ? zOrZero : (zOrZero + Math.getRandomInt(Math.floor(demiD), -Math.ceil(demiD)))) + zOffset;
|
|
14312
|
-
|
|
14313
|
-
if(avoidOverlap && size){
|
|
14314
|
-
|
|
14315
|
-
let itemW=nonull(size.w,0);
|
|
14316
|
-
let itemH=nonull(size.h,0);
|
|
14317
|
-
let itemD=nonull(size.d,0);
|
|
14318
|
-
|
|
14319
|
-
if(xOrZero/*includes case==0*/ && 0<itemW && contains(avoidOverlap,"x")) x=Math.floor(x/itemW)*itemW;
|
|
14320
|
-
if(yOrZero/*includes case==0*/ && 0<itemH && contains(avoidOverlap,"y")) y=Math.floor(y/itemH)*itemH;
|
|
14321
|
-
if(zOrZero/*includes case==0*/ && 0<itemD && contains(avoidOverlap,"z")) z=Math.floor(z/itemD)*itemD;
|
|
14322
|
-
|
|
14323
|
-
}
|
|
14324
|
-
|
|
14325
|
-
let b=positionZone.b;
|
|
14326
|
-
let g=positionZone.g;
|
|
14327
|
-
let a=positionZone.a;
|
|
14328
|
-
if(randomizeAngles){
|
|
14329
|
-
b=Math.random()*Math.PI*2;
|
|
14330
|
-
g=Math.random()*Math.PI*2;
|
|
14331
|
-
a=Math.random()*Math.PI*2;
|
|
14332
|
-
}
|
|
14333
|
-
|
|
14334
|
-
return {x:x, y:y, z:z, b:b, g:g, a:a, parallax:positionZone.parallax};
|
|
14335
|
-
}
|
|
14336
|
-
|
|
14337
|
-
|
|
14338
|
-
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
14339
|
-
/*public static*/window.isZoneCollidingWithZone=function(zone1, zone2){
|
|
14340
|
-
|
|
14341
|
-
let demiWidth1=zone1.w*.5;
|
|
14342
|
-
let demiWidth2=zone2.w*.5;
|
|
14343
|
-
|
|
14344
|
-
let xMin1=zone1.x-demiWidth1;
|
|
14345
|
-
let xMax1=zone1.x+demiWidth1;
|
|
14346
|
-
let xMin2=zone2.x-demiWidth2;
|
|
14347
|
-
let xMax2=zone2.x+demiWidth2;
|
|
14348
|
-
|
|
14349
|
-
//OLD : let widthsOverlap=((xMin2<xMin1 && xMin1<xMax2) || (xMin2<xMax1 && xMax1<xMax2)
|
|
14350
|
-
// || (xMin1<xMin2 && xMin2<xMax1) || (xMin1<xMax2 && xMax2<xMax1));
|
|
14351
|
-
|
|
14352
|
-
let widthsOverlap=(xMin1<xMax2 && xMin2<xMax1);
|
|
14353
|
-
|
|
14354
|
-
|
|
14355
|
-
let demiHeight1=zone1.h*.5;
|
|
14356
|
-
let demiHeight2=zone2.h*.5;
|
|
14357
|
-
|
|
14358
|
-
let yMin1=zone1.y-demiHeight1;
|
|
14359
|
-
let yMax1=zone1.y+demiHeight1;
|
|
14360
|
-
let yMin2=zone2.y-demiHeight2;
|
|
14361
|
-
let yMax2=zone2.y+demiHeight2;
|
|
14362
|
-
|
|
14363
|
-
//OLD : let heightsOverlap=((yMin1<yMin2 && yMin2<yMax1) || (yMin1<yMax2 && yMax2<yMax1)
|
|
14364
|
-
// || (yMin2<yMin1 && yMin1<yMax2) || (yMin2<yMax1 && yMax1<yMax2));
|
|
14365
|
-
|
|
14366
|
-
let heightsOverlap=(yMin1<yMax2 && yMin2<yMax1);
|
|
14367
|
-
|
|
14368
|
-
return widthsOverlap && heightsOverlap;
|
|
14369
|
-
}
|
|
14370
|
-
|
|
14371
|
-
|
|
14372
|
-
|
|
14373
14174
|
// CAUTION : ONLY HANDLES «center-positionned» ZONES !
|
|
14374
14175
|
/*public static*/window.getIntersectionZone=function(zone1, zone2){
|
|
14375
14176
|
if(!isZoneCollidingWithZone(zone1, zone2)) return null;
|
|
@@ -14564,10 +14365,10 @@ function rayVsUnitSphereClosestPoint(p, r) {
|
|
|
14564
14365
|
// MUST REMAIN AT THE END OF THIS LIBRARY FILE !
|
|
14565
14366
|
|
|
14566
14367
|
AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
|
|
14567
|
-
/*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)»*/
|
|
14568
14369
|
/*-----------------------------------------------------------------------------*/
|
|
14569
14370
|
|
|
14570
|
-
/*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)»*/
|
|
14571
14372
|
/*-----------------------------------------------------------------------------*/
|
|
14572
14373
|
|
|
14573
14374
|
|
|
@@ -14713,11 +14514,11 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
14713
14514
|
|
|
14714
14515
|
|
|
14715
14516
|
|
|
14716
|
-
/*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)»*/
|
|
14717
14518
|
/*-----------------------------------------------------------------------------*/
|
|
14718
14519
|
|
|
14719
14520
|
|
|
14720
|
-
/* ## Utility methods in a javascript, for AORTAC subsystem (
|
|
14521
|
+
/* ## Utility methods in a javascript, for AORTAC subsystem (client)
|
|
14721
14522
|
*
|
|
14722
14523
|
* This set of methods gathers utility generic-purpose methods usable in any JS project.
|
|
14723
14524
|
* Several authors of snippets published freely on the Internet contributed to this library.
|
|
@@ -14740,163 +14541,584 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
|
14740
14541
|
if(typeof(window)==="undefined") window=global;
|
|
14741
14542
|
|
|
14742
14543
|
|
|
14544
|
+
|
|
14545
|
+
|
|
14743
14546
|
// ==================================================================================================================
|
|
14744
14547
|
|
|
14745
14548
|
|
|
14746
|
-
// AORTAC
|
|
14549
|
+
// AORTAC CLIENT
|
|
14550
|
+
|
|
14747
14551
|
|
|
14748
14552
|
|
|
14749
14553
|
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
|
14750
14554
|
|
|
14751
|
-
|
|
14555
|
+
const AORTAC_CLIENT_FORCE_SSL_USAGE=true;
|
|
14752
14556
|
|
|
14753
14557
|
// New implementation :
|
|
14754
|
-
|
|
14755
|
-
class
|
|
14558
|
+
|
|
14559
|
+
class AORTACClientCell{
|
|
14756
14560
|
|
|
14757
|
-
constructor(
|
|
14561
|
+
constructor(serverCellOrigin, model, view, isNodeContext=false){
|
|
14562
|
+
|
|
14563
|
+
this.clientId=getUUID();
|
|
14758
14564
|
|
|
14759
14565
|
this.model=model;
|
|
14760
|
-
this.
|
|
14761
|
-
|
|
14762
|
-
this.quorumNumber=quorumNumber;
|
|
14763
|
-
this.selfOrigin=selfOrigin;
|
|
14764
|
-
const infos=splitURL(this.selfOrigin);
|
|
14765
|
-
|
|
14766
|
-
//DBG
|
|
14767
|
-
lognow("infos:::::::::::::",infos);
|
|
14566
|
+
this.view=view;
|
|
14567
|
+
this.isNodeContext=isNodeContext;
|
|
14768
14568
|
|
|
14569
|
+
const infos=splitURL(serverCellOrigin);
|
|
14570
|
+
|
|
14769
14571
|
this.protocol=nonull(infos.protocol,"ws");
|
|
14770
14572
|
this.host=nonull(infos.host,"localhost");
|
|
14771
14573
|
this.port=nonull(infos.port,"30000");
|
|
14772
|
-
this.
|
|
14773
|
-
|
|
14774
|
-
this.server=null;
|
|
14775
|
-
this.incomingCells={};
|
|
14574
|
+
this.usesSSL=AORTAC_CLIENT_FORCE_SSL_USAGE;
|
|
14776
14575
|
|
|
14777
|
-
this.
|
|
14778
|
-
this.outcomingCells={};
|
|
14779
|
-
const self=this;
|
|
14780
|
-
foreach(this.outcomingCellsOrigins,outcomingCellOrigin=>{
|
|
14781
|
-
self.outcomingCells[outcomingCellOrigin]={connected:false,client:null};
|
|
14782
|
-
});
|
|
14783
|
-
this.outcomingNetworkProber=null;
|
|
14576
|
+
this.startTime=null;
|
|
14784
14577
|
|
|
14785
|
-
this.
|
|
14786
|
-
|
|
14787
|
-
"outcoming":{"protocol":{}},
|
|
14788
|
-
"both":{"protocol":{}},
|
|
14789
|
-
};
|
|
14790
|
-
|
|
14791
|
-
this.cellsOverview={};
|
|
14578
|
+
this.initialClientInstance=null;
|
|
14579
|
+
this.clientInstances={};
|
|
14792
14580
|
|
|
14793
|
-
this.startTime=null;
|
|
14794
14581
|
|
|
14795
|
-
|
|
14796
|
-
|
|
14797
|
-
|
|
14798
|
-
this.
|
|
14582
|
+
|
|
14583
|
+
this.ready=false;
|
|
14584
|
+
|
|
14585
|
+
this.lobuleZone=new ClientLobule();
|
|
14799
14586
|
|
|
14587
|
+
|
|
14800
14588
|
}
|
|
14589
|
+
|
|
14590
|
+
|
|
14591
|
+
start(manifestationZone){
|
|
14592
|
+
|
|
14593
|
+
const self=this;
|
|
14801
14594
|
|
|
14802
|
-
start(){
|
|
14803
14595
|
|
|
14596
|
+
this.manifestationZone=manifestationZone;
|
|
14804
14597
|
this.startTime=getNow();
|
|
14805
|
-
this.server=this.launchServerForIncomingConnections();
|
|
14806
|
-
|
|
14807
|
-
if(empty(this.outcomingCellsOrigins) || (getArraySize(this.outcomingCellsOrigins)==1 && contains(this.outcomingCellsOrigins, this.selfOrigin))){
|
|
14808
|
-
// TRACE
|
|
14809
|
-
lognow("ERROR : Server cell must have an outcoming link to at least one other server cell of the blob. Aborting outcoming network probing.");
|
|
14810
|
-
}else{
|
|
14811
|
-
this.outcomingNetworkProber=new PeriodicalExecuter(this.probeOutcomingNetwork, AORTAC_SERVER_CELL_PERIODICAL_CONNECTIVITY_CHECK_MILLIS, this);
|
|
14812
|
-
}
|
|
14813
14598
|
|
|
14814
|
-
this.handleCommonListeners();
|
|
14815
|
-
|
|
14816
|
-
// TRACE
|
|
14817
|
-
lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
|
|
14818
|
-
|
|
14819
|
-
return this;
|
|
14820
|
-
}
|
|
14821
|
-
|
|
14822
|
-
/*private*/launchServerForIncomingConnections(){
|
|
14823
|
-
const self=this;
|
|
14824
14599
|
|
|
14825
|
-
const server=initNodeServerInfrastructureWrapper(
|
|
14826
|
-
// On each client connection :
|
|
14827
|
-
null,
|
|
14828
|
-
// On client finalization :
|
|
14829
|
-
function(server){
|
|
14830
14600
|
|
|
14831
|
-
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
14835
|
-
|
|
14836
|
-
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
});
|
|
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...");
|
|
14609
|
+
|
|
14610
|
+
initialClientInstanceLocal.socketToServerClientInstance=socketToServerClientInstance;
|
|
14842
14611
|
|
|
14612
|
+
// INITIATE CLIENT HELLO SEQUENCE
|
|
14613
|
+
self.initiateClientHelloSequence();
|
|
14843
14614
|
|
|
14844
|
-
// HANDLE HELLO SEQUENCE
|
|
14845
|
-
self.
|
|
14615
|
+
// HANDLE CLIENT HELLO SEQUENCE
|
|
14616
|
+
self.handleClientHelloSequence(resolve);
|
|
14617
|
+
|
|
14846
14618
|
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
|
|
14619
|
+
}, self.protocol+"://"+self.host, self.port, self.usesSSL);
|
|
14620
|
+
self.initialClientInstance.client.start();
|
|
14621
|
+
|
|
14622
|
+
});
|
|
14850
14623
|
}
|
|
14851
14624
|
|
|
14852
|
-
/*private*/probeOutcomingNetwork(){
|
|
14853
|
-
const numberOfTotalServers=getArraySize(this.outcomingCells);
|
|
14854
|
-
let numberOfConnectedOutcomingServers=0;
|
|
14855
|
-
foreach(this.outcomingCells, outcomingCell=>{
|
|
14856
|
-
numberOfConnectedOutcomingServers++
|
|
14857
|
-
},outcomingCell=>outcomingCell.connected);
|
|
14858
|
-
|
|
14859
|
-
// TRACE
|
|
14860
|
-
lognow(`INFO : Number of connected outcoming servers : ${numberOfConnectedOutcomingServers}/${numberOfTotalServers}`);
|
|
14861
|
-
|
|
14862
|
-
if(numberOfTotalServers<=numberOfConnectedOutcomingServers){
|
|
14863
|
-
this.outcomingNetworkProber.stop();
|
|
14864
|
-
|
|
14865
|
-
// TRACE
|
|
14866
|
-
lognow(`INFO : All outcoming cells connected. Stopping outcoming network probing.`);
|
|
14867
14625
|
|
|
14868
|
-
|
|
14869
|
-
|
|
14626
|
+
// ===================================================================================================
|
|
14627
|
+
// SEQUENCES
|
|
14628
|
+
// ===================================================================================================
|
|
14870
14629
|
|
|
14871
|
-
|
|
14872
|
-
|
|
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
|
+
}
|
|
14873
14652
|
|
|
14874
|
-
|
|
14653
|
+
/*private*/handleClientHelloSequence(resolve){
|
|
14875
14654
|
|
|
14876
|
-
|
|
14877
|
-
|
|
14655
|
+
const self=this;
|
|
14656
|
+
this.initialClientInstance.socketToServerClientInstance.receive("protocol", (messageParam, clientSocket)=>{
|
|
14878
14657
|
|
|
14879
|
-
const
|
|
14880
|
-
const outcomingCellProtocol=nonull(infos.protocol,"ws");
|
|
14881
|
-
const outcomingCellHost=nonull(infos.host,"localhost");
|
|
14882
|
-
const outcomingCellPort=nonull(infos.port,"40000");
|
|
14658
|
+
const message=JSON.recycle(messageParam);
|
|
14883
14659
|
|
|
14884
|
-
const
|
|
14885
|
-
|
|
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
|
+
|
|
14886
15021
|
// On-receive message listeners handling ;
|
|
14887
|
-
foreach(
|
|
14888
|
-
|
|
14889
|
-
|
|
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);
|
|
14890
15034
|
});
|
|
14891
15035
|
});
|
|
14892
|
-
|
|
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);
|
|
15093
|
+
const outcomingCellProtocol=nonull(infos.protocol,"ws");
|
|
15094
|
+
const outcomingCellHost=nonull(infos.host,"localhost");
|
|
15095
|
+
const outcomingCellPort=nonull(infos.port,"40000");
|
|
15096
|
+
|
|
15097
|
+
const clientInstance=initClient(true, false, (socketToServerClientInstance)=>{
|
|
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
|
+
|
|
15105
|
+
// On-receive message listeners handling ;
|
|
15106
|
+
foreach(channelsNames, channelName=>{
|
|
14893
15107
|
socketToServerClientInstance.receive(channelName, (message, clientSocket)=>{
|
|
15108
|
+
|
|
15109
|
+
// DBG
|
|
15110
|
+
lognow("DEBUG : Server cell receives message from outcoming...");
|
|
15111
|
+
|
|
15112
|
+
self.executeListeners("outcoming",channelName, message, clientSocket);
|
|
15113
|
+
|
|
15114
|
+
// DBG
|
|
15115
|
+
lognow("DEBUG : Server cell receives message from both (out)...");
|
|
15116
|
+
|
|
14894
15117
|
self.executeListeners("both",channelName, message, clientSocket);
|
|
14895
15118
|
});
|
|
14896
15119
|
});
|
|
14897
|
-
|
|
14898
15120
|
|
|
14899
|
-
// INITIATE HELLO SEQUENCE
|
|
15121
|
+
// INITIATE SERVER CELL HELLO SEQUENCE
|
|
14900
15122
|
self.initiateHelloSequence(socketToServerClientInstance);
|
|
14901
15123
|
|
|
14902
15124
|
// We are connected:
|
|
@@ -14920,47 +15142,68 @@ class AORTACServerCell{
|
|
|
14920
15142
|
|
|
14921
15143
|
}
|
|
14922
15144
|
|
|
14923
|
-
|
|
14924
15145
|
/*private*/handleCommonListeners(){
|
|
14925
|
-
|
|
14926
15146
|
// HANDLE CELL IS READY SEQUENCE
|
|
14927
15147
|
this.handleCellIsReadySequence();
|
|
14928
|
-
|
|
14929
15148
|
// HANDLE PARTITION SEQUENCE
|
|
14930
15149
|
this.handlePartitionSequence();
|
|
14931
15150
|
|
|
14932
|
-
|
|
15151
|
+
|
|
15152
|
+
// ******************************************
|
|
15153
|
+
// CLIENT
|
|
15154
|
+
// HANDLE CLIENT MODEL INITIAL POPULATION SEQUENCE
|
|
15155
|
+
this.handleGetServersForZoneRequest();
|
|
15156
|
+
|
|
15157
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
15158
|
+
this.handleReconnectedClientHelloSequence();
|
|
14933
15159
|
|
|
15160
|
+
}
|
|
15161
|
+
|
|
14934
15162
|
|
|
14935
15163
|
/*private*/executeListeners(outletName, channelName, message, clientSocket){
|
|
14936
15164
|
const onReceiveMessageListeners=this.onReceiveMessageListeners[outletName][channelName];
|
|
14937
15165
|
if(!onReceiveMessageListeners) return;
|
|
14938
15166
|
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
|
|
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");
|
|
14953
15190
|
}
|
|
14954
15191
|
|
|
14955
15192
|
|
|
14956
15193
|
/*private*/sendMessageToBlob(channelName, message,
|
|
14957
|
-
broadcastConfig={
|
|
15194
|
+
broadcastConfig={
|
|
15195
|
+
isOriginatingCell:false,
|
|
15196
|
+
destinationCellsOrigins:null,
|
|
15197
|
+
excludeIncomingServersCellsAndClientsInTransmission:true,
|
|
15198
|
+
isRequest:false,
|
|
15199
|
+
contactAllQuorumBlob:false
|
|
15200
|
+
},
|
|
15201
|
+
overridingMessageType=null){
|
|
14958
15202
|
|
|
14959
|
-
const self=this;
|
|
14960
|
-
|
|
14961
15203
|
if(broadcastConfig){
|
|
14962
15204
|
if(broadcastConfig.isOriginatingCell){
|
|
14963
15205
|
message.originatingCellOrigin=this.selfOrigin;
|
|
15206
|
+
message.originatingPartitionId=this.partitionId;
|
|
14964
15207
|
}
|
|
14965
15208
|
if(broadcastConfig.isRequest){
|
|
14966
15209
|
message.isRequest=true;
|
|
@@ -14973,10 +15216,36 @@ class AORTACServerCell{
|
|
|
14973
15216
|
if(!message.visitedCells)
|
|
14974
15217
|
message.visitedCells=[];
|
|
14975
15218
|
else if(contains(message.visitedCells,this.selfOrigin))
|
|
14976
|
-
return;
|
|
15219
|
+
return null;
|
|
14977
15220
|
message.visitedCells.push(this.selfOrigin);
|
|
14978
15221
|
|
|
14979
|
-
|
|
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){
|
|
14980
15249
|
foreach(this.incomingCells,(incomingCell)=>{
|
|
14981
15250
|
// As a server, we send (forward) the message to the currently iterated upon client that is connected to us :
|
|
14982
15251
|
incomingCell.server.send(channelName, message, null, incomingCell.clientSocket);
|
|
@@ -14984,7 +15253,6 @@ class AORTACServerCell{
|
|
|
14984
15253
|
(incomingCell.connected && !contains(message.visitedCells, incomingCellOrigin))
|
|
14985
15254
|
);
|
|
14986
15255
|
}
|
|
14987
|
-
|
|
14988
15256
|
foreach(this.outcomingCells,(outcomingCell)=>{
|
|
14989
15257
|
// As a client, we send (forward) the message to the currently iterated upon server we are connected to :
|
|
14990
15258
|
outcomingCell.socketToServerClientInstance.send(channelName, message);
|
|
@@ -14992,49 +15260,84 @@ class AORTACServerCell{
|
|
|
14992
15260
|
(outcomingCell.connected && !contains(message.visitedCells, outcomingCellOrigin))
|
|
14993
15261
|
);
|
|
14994
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
|
+
}
|
|
14995
15279
|
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
// When we have received the message :
|
|
15017
|
-
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){
|
|
15018
15300
|
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
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
|
+
}
|
|
15024
15309
|
}
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
|
|
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
|
+
};
|
|
15032
15327
|
|
|
15328
|
+
|
|
15329
|
+
|
|
15330
|
+
return blobResponseListener;
|
|
15033
15331
|
}
|
|
15332
|
+
|
|
15333
|
+
|
|
15034
15334
|
|
|
15335
|
+
// ===================================================================================================
|
|
15336
|
+
// SERVER CELLS
|
|
15337
|
+
// ===================================================================================================
|
|
15035
15338
|
|
|
15036
15339
|
// ==========================================================
|
|
15037
|
-
// HELLO SEQUENCE
|
|
15340
|
+
// SERVER CELL HELLO SEQUENCE
|
|
15038
15341
|
|
|
15039
15342
|
/*private*/initiateHelloSequence(socketToServerClientInstance){
|
|
15040
15343
|
|
|
@@ -15047,28 +15350,30 @@ class AORTACServerCell{
|
|
|
15047
15350
|
}
|
|
15048
15351
|
|
|
15049
15352
|
|
|
15050
|
-
/*private*/
|
|
15353
|
+
/*private*/handleServerCellHelloSequence(){
|
|
15051
15354
|
|
|
15052
15355
|
// 2- We wait to receive the hello request of the incoming cell connection :
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
15356
|
+
this.onReceiveMessageListeners["both"]["protocol"]["helloRequest"]={
|
|
15357
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15056
15358
|
|
|
15057
|
-
const cellOriginToCheck=
|
|
15359
|
+
const cellOriginToCheck=messageParam.originatingCellOrigin;
|
|
15058
15360
|
|
|
15059
15361
|
// TRACE
|
|
15060
|
-
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...`);
|
|
15061
15363
|
|
|
15062
15364
|
|
|
15063
15365
|
selfParam.incomingCells[cellOriginToCheck]={
|
|
15064
15366
|
connected:true,
|
|
15065
|
-
server:server,
|
|
15367
|
+
server:serverWrapper.server,
|
|
15066
15368
|
// DO NOT USE TO SEND/RECEIVE ANYTHINIG !
|
|
15067
15369
|
// For this, use the server attribute (+ the clientSocket as argument) instead.
|
|
15068
15370
|
clientSocket:clientSocket,
|
|
15069
15371
|
};
|
|
15070
15372
|
|
|
15071
|
-
|
|
15373
|
+
|
|
15374
|
+
// NO SERVER CELL HELLO CONFIRMATION SENT BACK TO THE OTHER SERVER CELL (to save time)
|
|
15375
|
+
|
|
15376
|
+
},
|
|
15072
15377
|
listenerMessageType:"helloRequest"
|
|
15073
15378
|
};
|
|
15074
15379
|
|
|
@@ -15093,18 +15398,14 @@ class AORTACServerCell{
|
|
|
15093
15398
|
}
|
|
15094
15399
|
|
|
15095
15400
|
/*private*/handleCellIsReadySequence(){
|
|
15096
|
-
getOrCreateEmptyAttribute(
|
|
15097
|
-
this.onReceiveMessageListeners["both"]["protocol"],"cellIsReady")["forRequestsIssuedByOthers"]={
|
|
15098
|
-
execute:(selfParam, message, server, clientSocket)=>{
|
|
15099
15401
|
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
|
|
15402
|
+
this.onReceiveMessageListeners["both"]["protocol"]["cellIsReady"]={
|
|
15403
|
+
|
|
15404
|
+
execute:(selfParam, messageParam, serverWrapper, clientSocket)=>{
|
|
15405
|
+
|
|
15406
|
+
selfParam.cellsOverview[messageParam.originatingCellOrigin]={ready:true,startTime:messageParam.startTime};
|
|
15103
15407
|
|
|
15104
|
-
|
|
15105
|
-
// lognow("INFO : Updated cells overview for this cell ("+selfParam.selfOrigin+") :",selfParam.cellsOverview);
|
|
15106
|
-
// lognow("DEBUG : selfParam.outcomingCells :",Object.keys(selfParam.outcomingCells));
|
|
15107
|
-
// lognow("DEBUG : selfParam.incomingCells :",Object.keys(selfParam.incomingCells));
|
|
15408
|
+
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
15108
15409
|
|
|
15109
15410
|
// INITIATE QUORUM IS REACHED SEQUENCE
|
|
15110
15411
|
selfParam.initiateQuorumIsReachedSequenceIfNecessary();
|
|
@@ -15130,11 +15431,11 @@ class AORTACServerCell{
|
|
|
15130
15431
|
const quorumCells=copy(this.cellsOverview);
|
|
15131
15432
|
|
|
15132
15433
|
// TRACE
|
|
15133
|
-
lognow("INFO : Quorum is reached and this is the latest started cell. quorumCells
|
|
15134
|
-
|
|
15135
|
-
this.
|
|
15136
|
-
|
|
15137
|
-
|
|
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);
|
|
15138
15439
|
|
|
15139
15440
|
}
|
|
15140
15441
|
|
|
@@ -15145,249 +15446,694 @@ class AORTACServerCell{
|
|
|
15145
15446
|
// ==========================================================
|
|
15146
15447
|
// MODEL MANAGEMENT
|
|
15147
15448
|
|
|
15148
|
-
/*private*/
|
|
15449
|
+
/*private*/async launchModelPartitionBlobSequence(quorumCells){
|
|
15149
15450
|
|
|
15150
15451
|
const self=this;
|
|
15151
15452
|
|
|
15152
|
-
|
|
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});
|
|
15153
15681
|
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15160
|
-
controller.initializeModelForAORTACNode(model);
|
|
15161
|
-
|
|
15162
|
-
// This must return the asked number of partitions, indexed by partition id :
|
|
15163
|
-
// CAUTION : The partition function MUST return ALL the objects in a partition, WHATEVER THEIR NESTING LEVEL !!!
|
|
15164
|
-
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);
|
|
15165
15688
|
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
// We send the models objects to the required cells :
|
|
15171
|
-
let i=1;
|
|
15172
|
-
foreach(quorumCells, (quorumCell,quorumCellOrigin)=>{
|
|
15689
|
+
selfParam2.lobuleZone.clear(clientIdLocal);
|
|
15690
|
+
|
|
15691
|
+
});
|
|
15692
|
+
|
|
15173
15693
|
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
const message=JSON.decycle({type:"modelPartition",partition:modelPartition});
|
|
15178
|
-
|
|
15179
|
-
// TRACE
|
|
15180
|
-
lognow(`INFO : Cell ${self.selfOrigin} is sending a partition to cell ${quorumCellOrigin}...`,message);
|
|
15181
|
-
|
|
15182
|
-
self.sendMessageToBlob("protocol", message,
|
|
15183
|
-
{isOriginatingCell:true, destinationCellsOrigins:[quorumCellOrigin], includeIncomingConnectionInTransmission:false, isRequest:false});
|
|
15694
|
+
},
|
|
15695
|
+
listenerMessageType:"clientHello"
|
|
15696
|
+
};
|
|
15184
15697
|
|
|
15185
|
-
i++;
|
|
15186
|
-
},(quorumCell,quorumCellOrigin)=>quorumCellOrigin!=self.selfOrigin);
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
// Each quorum member cell is responsible for a model partition
|
|
15190
|
-
// Then the sattelite cells will be handling the duplication
|
|
15191
|
-
|
|
15192
15698
|
}
|
|
15193
|
-
|
|
15194
15699
|
|
|
15195
|
-
// HANDLE PARTITION SEQUENCE
|
|
15196
15700
|
|
|
15197
|
-
|
|
15701
|
+
// This is what other cells do when they receive a request for «getServersForZone» from a cell in the blob
|
|
15702
|
+
/*private*/handleGetServersForZoneRequest(){
|
|
15198
15703
|
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
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
|
+
|
|
15203
15730
|
|
|
15204
|
-
|
|
15205
|
-
selfParam.sendMessageToBlob("protocol",messageParam);
|
|
15731
|
+
const message={type:"getServersForZone.response", objectsCount:allObjectsCorrespondingToZoneInServerCell.length, answeringServerOrigin:selfParam.selfOrigin};
|
|
15206
15732
|
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
const message=JSON.recycle(messageParam);
|
|
15210
|
-
|
|
15211
|
-
selfParam.doOnModelPartitionReception(message.partition);
|
|
15212
|
-
|
|
15213
|
-
}
|
|
15733
|
+
// TRACE
|
|
15734
|
+
lognow(`INFO : Server cell ${selfParam.selfOrigin} is sending its origin to server cell ${messageParam.originatingCellOrigin}...: message:`, message);
|
|
15214
15735
|
|
|
15215
|
-
|
|
15216
|
-
listenerMessageType:"
|
|
15736
|
+
selfParam.replyToBlobRequest("protocol", messageParam, message);
|
|
15737
|
+
},listenerMessageType:"getServersForZone"
|
|
15217
15738
|
};
|
|
15218
15739
|
|
|
15219
15740
|
}
|
|
15220
15741
|
|
|
15221
15742
|
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
lognow(`INFO : Incoming partition for cell ${this.selfOrigin}. Updating local model...`,partition);
|
|
15226
|
-
lognow(`>>>>`,stringifyObject(JSON.decycle(partition.objects),1));
|
|
15743
|
+
// ----
|
|
15744
|
+
// HANDLE RECONNECTED CLIENT HELLO SEQUENCE
|
|
15745
|
+
handleReconnectedClientHelloSequence(){
|
|
15227
15746
|
|
|
15747
|
+
// Here we only want to collect the servers on which to reconnect to later :
|
|
15228
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
|
+
|
|
15229
15785
|
|
|
15230
15786
|
}
|
|
15231
15787
|
|
|
15232
15788
|
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
const self=this;
|
|
15236
|
-
|
|
15237
|
-
if(!currentObject){
|
|
15238
|
-
foreach(linkedObjects, obj=>{
|
|
15239
|
-
self.removeLinksOnSingleObject(linkedObjects, obj);
|
|
15240
|
-
});
|
|
15241
|
-
}else{
|
|
15242
|
-
self.removeLinksOnSingleObject(linkedObjects, currentObject);
|
|
15243
|
-
}
|
|
15244
|
-
|
|
15245
|
-
return linkedObjects;
|
|
15246
|
-
}
|
|
15789
|
+
// ==========================================================
|
|
15247
15790
|
|
|
15248
|
-
|
|
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
|
+
// }
|
|
15249
15827
|
|
|
15250
|
-
const self=this;
|
|
15251
|
-
foreach(currentObject, (attr,attrNameOrIndex)=>{
|
|
15252
|
-
// We only remove links to the objects not in the partition
|
|
15253
|
-
if(isClassObject(attr)){
|
|
15254
|
-
let aortacId=attr.aortacId;
|
|
15255
|
-
if(!aortacId){
|
|
15256
|
-
aortacId=getUUID();
|
|
15257
|
-
attr.aortacId=aortacId;
|
|
15258
|
-
}
|
|
15259
|
-
if(!contains(linkedObjects,attr)){
|
|
15260
|
-
currentObject[attrNameOrIndex]=aortacId+"@aortacId";
|
|
15261
|
-
// CAUTION : No need for recursive call here, because the partition function returns ALL the objects in a partition,
|
|
15262
|
-
// WHATEVER THEIR NESTING LEVEL !!!
|
|
15263
|
-
}
|
|
15264
|
-
}else{
|
|
15265
|
-
// However, we need a recursive call for any simple object or array that may reference a class object in another partition :
|
|
15266
|
-
self.removeLinksOnSingleObject(linkedObjects, attr);
|
|
15267
|
-
}
|
|
15268
|
-
},obj=>(isObject(obj) || isArray(obj)));
|
|
15269
15828
|
|
|
15270
|
-
}
|
|
15271
15829
|
|
|
15272
15830
|
|
|
15273
|
-
//
|
|
15274
|
-
|
|
15275
|
-
/*private*/
|
|
15831
|
+
// **********************************************************************
|
|
15832
|
+
|
|
15833
|
+
/*private*/partiallyPopulatedRootContainer(allObjectsCorrespondingToZoneInServerCell=[]){
|
|
15276
15834
|
|
|
15277
|
-
|
|
15278
|
-
lognow("DEBUG : collectDependencies()...",inputObjects);
|
|
15835
|
+
const rootContainerLocal=this.model.getRootContainer();
|
|
15279
15836
|
|
|
15837
|
+
const rootContainer=this.model.populateRootContainerWithObjectsSubset(rootContainerLocal, allObjectsCorrespondingToZoneInServerCell, true);
|
|
15838
|
+
rootContainer.isRootContainer=true;
|
|
15280
15839
|
|
|
15840
|
+
return rootContainer;
|
|
15281
15841
|
}
|
|
15282
|
-
|
|
15283
|
-
/*private*/repercutChangesIfNeeded(inputObjects){
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
////
|
|
15287
|
-
lognow("DEBUG : repercutChangesIfNeeded()...",inputObjects);
|
|
15288
15842
|
|
|
15289
|
-
|
|
15290
|
-
}
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
15843
|
}
|
|
15294
15844
|
|
|
15295
15845
|
|
|
15296
15846
|
|
|
15297
|
-
// ******************************************************************
|
|
15298
15847
|
|
|
15299
|
-
// Public static hydration method :
|
|
15300
|
-
window.ao=(incompleteModelObjectToDecorate)=>{
|
|
15301
|
-
|
|
15302
|
-
const localServerCell=window.aortacCServerNodeInstance;
|
|
15303
|
-
if(!localServerCell){
|
|
15304
|
-
// TRACE
|
|
15305
|
-
lognow("ERROR : No AORTACCServerNode singleton instance. Doing nothing on the model object.");
|
|
15306
|
-
return incompleteModelObjectToDecorate;
|
|
15307
|
-
}
|
|
15308
|
-
|
|
15309
|
-
const liveModelObjects=localServerCell.liveModelObjects;
|
|
15310
|
-
|
|
15311
|
-
// First we clone the object, for its attriutes values information :
|
|
15312
|
-
const clonedObject=clone(incompleteModelObjectToDecorate);
|
|
15313
|
-
|
|
15314
|
-
// Then we replace all its methods :
|
|
15315
|
-
foreach(clonedObject, (method, methodName)=>{
|
|
15316
|
-
clonedObject[methodName]=new Proxy(method,
|
|
15317
|
-
{
|
|
15318
|
-
apply: function(methodToEnhance, thisArg, argumentsList) {
|
|
15319
15848
|
|
|
15320
|
-
const inputObjects=[thisArg];
|
|
15321
|
-
inputObjects.push(...argumentsList);
|
|
15322
|
-
localServerCell.collectDependencies(inputObjects);
|
|
15323
|
-
|
|
15324
|
-
// // --- Treatment BEFORE function execution ---
|
|
15325
|
-
// console.log(`Calling function "${methodToEnhance.name}" with arguments: ${argumentsList}`);
|
|
15326
|
-
|
|
15327
|
-
// Call the original function (target) with its intended 'this' context (thisArg)
|
|
15328
|
-
// and arguments (argumentsList) using Reflect.apply
|
|
15329
|
-
const result = Reflect.apply(methodToEnhance, thisArg, argumentsList);
|
|
15330
|
-
|
|
15331
|
-
// // --- Treatment AFTER function execution ---
|
|
15332
|
-
// console.log(`Function "${methodToEnhance.name}" returned: ${result}`);
|
|
15333
|
-
|
|
15334
|
-
inputObjects.push(result);
|
|
15335
|
-
localServerCell.repercutChangesIfNeeded(inputObjects);
|
|
15336
15849
|
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
},attribute=>isFunction(attribute));
|
|
15850
|
+
|
|
15851
|
+
|
|
15852
|
+
|
|
15853
|
+
class ServerCellLobule{
|
|
15342
15854
|
|
|
15343
|
-
|
|
15344
|
-
|
|
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
|
+
}
|
|
15345
15918
|
|
|
15346
15919
|
|
|
15347
15920
|
|
|
15348
|
-
window.
|
|
15921
|
+
window.getAORTACServerCell=function(quorumNumber=1, selfOrigin="ws://127.0.0.1:40000", outcomingCellsOrigins=[], model, controller){
|
|
15349
15922
|
//return new AORTACServerNode("node_"+getUUID("short"), selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15350
|
-
if(window.
|
|
15923
|
+
if(window.aortacCServerCellInstance){
|
|
15351
15924
|
// TRACE
|
|
15352
|
-
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.");
|
|
15353
15926
|
}
|
|
15354
|
-
window.
|
|
15355
|
-
return window.
|
|
15927
|
+
window.aortacCServerCellInstance=new AORTACServerCell(quorumNumber, selfOrigin, outcomingCellsOrigins, model, controller);
|
|
15928
|
+
return window.aortacCServerCellInstance;
|
|
15356
15929
|
}
|
|
15357
15930
|
|
|
15358
15931
|
|
|
15359
15932
|
|
|
15360
15933
|
|
|
15934
|
+
// ===============================================================================================================================
|
|
15935
|
+
// ===============================================================================================================================
|
|
15936
|
+
// UTILITY METHODS
|
|
15361
15937
|
|
|
15362
15938
|
|
|
15939
|
+
// USAGE EXAMPLE :
|
|
15363
15940
|
|
|
15364
|
-
// ==================================================================================================================
|
|
15365
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
|
|
15366
15968
|
|
|
15367
|
-
// AORTAC CLIENT
|
|
15368
15969
|
|
|
15970
|
+
window.proxyClass=function(classToProxy,
|
|
15971
|
+
doOnAccess=(className, propertyName, parameter)=>{lognow(`Accessing ${className}.${propertyName}:`,parameter);}){
|
|
15369
15972
|
|
|
15973
|
+
const className=classToProxy.name;
|
|
15370
15974
|
|
|
15371
|
-
|
|
15975
|
+
// Build the Proxy handler
|
|
15976
|
+
const proxyHandler={
|
|
15372
15977
|
|
|
15373
|
-
//
|
|
15978
|
+
// Intercepts: obj.property AND obj.method()
|
|
15979
|
+
get(concernedObject, property, thisArg){
|
|
15980
|
+
const value=Reflect.get(concernedObject, property, thisArg);
|
|
15374
15981
|
|
|
15375
|
-
|
|
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=()=>{
|
|
15376
16065
|
|
|
15377
|
-
|
|
16066
|
+
const aortacConfig=GAME_CONFIG.aortac;
|
|
16067
|
+
if(nothing(aortacConfig)){
|
|
16068
|
+
// TRACE
|
|
16069
|
+
lognow("WARN : No AORTAC config, Aborting..");
|
|
16070
|
+
return null;
|
|
16071
|
+
}
|
|
15378
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+"».");
|
|
15379
16081
|
}
|
|
15380
16082
|
|
|
15381
|
-
|
|
16083
|
+
return currentLevelPrototypeName;
|
|
16084
|
+
};
|
|
15382
16085
|
|
|
15383
16086
|
|
|
15384
16087
|
|
|
15385
16088
|
|
|
15386
|
-
window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model, view, isNodeContext=false){
|
|
15387
|
-
//return new AORTACClient("client_"+getUUID(), serverNodeOrigin, model, view, isNodeContext);
|
|
15388
|
-
return new AORTACClientCell(serverNodeOrigin, model, view, isNodeContext);
|
|
15389
|
-
}
|
|
15390
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
|
+
//};
|
|
15391
16137
|
|
|
15392
16138
|
|
|
15393
16139
|
|
|
@@ -16103,7 +16849,7 @@ WebsocketImplementation={
|
|
|
16103
16849
|
},
|
|
16104
16850
|
|
|
16105
16851
|
|
|
16106
|
-
/*
|
|
16852
|
+
/*public*//*static*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
|
|
16107
16853
|
|
|
16108
16854
|
const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
|
|
16109
16855
|
|
|
@@ -16300,12 +17046,11 @@ WebsocketImplementation={
|
|
|
16300
17046
|
|
|
16301
17047
|
|
|
16302
17048
|
|
|
16303
|
-
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){
|
|
16304
17050
|
|
|
16305
17051
|
const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
|
|
16306
17052
|
|
|
16307
17053
|
|
|
16308
|
-
|
|
16309
17054
|
if(typeof(https)==="undefined"){
|
|
16310
17055
|
// TRACE
|
|
16311
17056
|
console.log("«https» SERVER library not called yet, calling it now.");
|
|
@@ -16397,8 +17142,6 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16397
17142
|
|
|
16398
17143
|
const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
|
|
16399
17144
|
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
17145
|
let listenableServer;
|
|
16403
17146
|
if(sslOptions){
|
|
16404
17147
|
let httpsServer=https.createServer(sslOptions, handler).listen(port);
|
|
@@ -16419,7 +17162,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16419
17162
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
16420
17163
|
server.onConnectionToClient((serverParam, clientSocketParam)=>{
|
|
16421
17164
|
if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
|
|
16422
|
-
});
|
|
17165
|
+
}, doOnConnectionLost);
|
|
16423
17166
|
|
|
16424
17167
|
|
|
16425
17168
|
server.onFinalize((serverParam)=>{
|
|
@@ -16434,15 +17177,13 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
|
16434
17177
|
// TRACE
|
|
16435
17178
|
console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
|
|
16436
17179
|
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
17180
|
|
|
16440
17181
|
|
|
16441
17182
|
return server;
|
|
16442
17183
|
}
|
|
16443
17184
|
|
|
16444
17185
|
|
|
16445
|
-
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
|
|
17186
|
+
initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, doOnConnectionLost=null,
|
|
16446
17187
|
/*OPTIONAL*/portParam,
|
|
16447
17188
|
/*OPTIONAL*/certPathParam,
|
|
16448
17189
|
/*OPTIONAL*/keyPathParam){
|
|
@@ -16495,13 +17236,11 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16495
17236
|
|
|
16496
17237
|
|
|
16497
17238
|
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
17239
|
const aotraNodeServer={config:serverConfig};
|
|
16501
17240
|
aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
|
|
16502
17241
|
|
|
16503
17242
|
if(isHashAsked){
|
|
16504
|
-
// We
|
|
17243
|
+
// We instantiate a temporary persister just to read the key hash file:
|
|
16505
17244
|
const persister=getPersister("./");
|
|
16506
17245
|
let persisterIdSplits=persisterId.split("@");
|
|
16507
17246
|
if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
|
|
@@ -16584,9 +17323,9 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16584
17323
|
}
|
|
16585
17324
|
}
|
|
16586
17325
|
|
|
16587
|
-
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
|
|
17326
|
+
aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, doOnConnectionLost, sslOptions);
|
|
17327
|
+
|
|
16588
17328
|
|
|
16589
|
-
|
|
16590
17329
|
return aotraNodeServer;
|
|
16591
17330
|
};
|
|
16592
17331
|
|
|
@@ -16649,7 +17388,10 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
|
16649
17388
|
// },REFRESH_SCREENSHOTS_MILLIS);
|
|
16650
17389
|
//
|
|
16651
17390
|
//
|
|
16652
|
-
// },
|
|
17391
|
+
// },
|
|
17392
|
+
// // On client connection lost :
|
|
17393
|
+
// (clientId)=>{lognow(`INFO : Connection to client id «${clientId}» was lost.`);},
|
|
17394
|
+
// portParam,certPathParam,keyPathParam);
|
|
16653
17395
|
//
|
|
16654
17396
|
//
|
|
16655
17397
|
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
|
@@ -17117,8 +17859,8 @@ initClient=function(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false
|
|
|
17117
17859
|
// CAUTION : MUST BE CALLED ONLY ONCE !
|
|
17118
17860
|
socketToServerClientInstance.onConnectionToServer(()=>{
|
|
17119
17861
|
if(doOnServerConnection){
|
|
17120
|
-
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance]);
|
|
17121
|
-
else doOnServerConnection(socketToServerClientInstance);
|
|
17862
|
+
if(selfParam) doOnServerConnection.apply(selfParam,[socketToServerClientInstance, aotraClient]);
|
|
17863
|
+
else doOnServerConnection(socketToServerClientInstance, aotraClient);
|
|
17122
17864
|
}
|
|
17123
17865
|
|
|
17124
17866
|
|
|
@@ -17215,8 +17957,9 @@ class ClientReceptionEntryPoint{
|
|
|
17215
17957
|
|
|
17216
17958
|
|
|
17217
17959
|
// We check if the message matches the required message type :
|
|
17218
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17219
|
-
&&
|
|
17960
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17961
|
+
&& dataWrapped.data
|
|
17962
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
17220
17963
|
return;
|
|
17221
17964
|
}
|
|
17222
17965
|
|
|
@@ -17240,8 +17983,12 @@ class ClientReceptionEntryPoint{
|
|
|
17240
17983
|
if(this.listenerConfig && this.listenerConfig.destroyListenerAfterReceiving)
|
|
17241
17984
|
remove(clientReceptionEntryPoints, this);
|
|
17242
17985
|
|
|
17243
|
-
if(this.doOnIncomingMessage)
|
|
17244
|
-
|
|
17986
|
+
if(this.doOnIncomingMessage){
|
|
17987
|
+
|
|
17988
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
17989
|
+
|
|
17990
|
+
this.doOnIncomingMessage(dataRestored, clientSocket);
|
|
17991
|
+
}
|
|
17245
17992
|
|
|
17246
17993
|
}
|
|
17247
17994
|
|
|
@@ -17284,7 +18031,9 @@ class ClientInstance{
|
|
|
17284
18031
|
// I-
|
|
17285
18032
|
//
|
|
17286
18033
|
self.receive(channelNameForResponse, doOnIncomingMessageForResponse,
|
|
17287
|
-
|
|
18034
|
+
{messageId:messageId, destroyListenerAfterReceiving:true},
|
|
18035
|
+
clientsRoomsTag, listenerId,
|
|
18036
|
+
);
|
|
17288
18037
|
//
|
|
17289
18038
|
|
|
17290
18039
|
return resultPromise;
|
|
@@ -17299,7 +18048,7 @@ class ClientInstance{
|
|
|
17299
18048
|
}
|
|
17300
18049
|
|
|
17301
18050
|
// II-
|
|
17302
|
-
receive(channelNameParam, doOnIncomingMessage,
|
|
18051
|
+
receive(channelNameParam, doOnIncomingMessage, listenerConfig={destroyListenerAfterReceiving:false, listenerMessageType:null}, clientsRoomsTag=null, entryPointId=null){
|
|
17303
18052
|
const self=this;
|
|
17304
18053
|
|
|
17305
18054
|
// // DBG
|
|
@@ -17339,8 +18088,11 @@ class ClientInstance{
|
|
|
17339
18088
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17340
18089
|
|
|
17341
18090
|
// Channel information is stored in exchanged data :
|
|
17342
|
-
let dataWrapped={channelName:channelNameParam, data:data};
|
|
17343
|
-
|
|
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!)
|
|
17344
18096
|
dataWrapped=stringifyObject(dataWrapped);
|
|
17345
18097
|
|
|
17346
18098
|
// TODO : FIXME : Use one single interface !
|
|
@@ -17472,8 +18224,9 @@ class ServerReceptionEntryPoint{
|
|
|
17472
18224
|
if(!isClientInRoom) return;
|
|
17473
18225
|
|
|
17474
18226
|
// We check if the message matches the required message type :
|
|
17475
|
-
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
17476
|
-
|
|
18227
|
+
if( this.listenerConfig && this.listenerConfig.listenerMessageType
|
|
18228
|
+
&& dataWrapped.data
|
|
18229
|
+
&& dataWrapped.data.type && this.listenerConfig.listenerMessageType!==dataWrapped.data.type){
|
|
17477
18230
|
return;
|
|
17478
18231
|
}
|
|
17479
18232
|
|
|
@@ -17481,7 +18234,11 @@ class ServerReceptionEntryPoint{
|
|
|
17481
18234
|
// // DBG
|
|
17482
18235
|
// lognow("(SERVER) this.doOnIncomingMessage:");
|
|
17483
18236
|
|
|
17484
|
-
|
|
18237
|
+
const dataLocal=dataWrapped.data;
|
|
18238
|
+
|
|
18239
|
+
const dataRestored=restoreClassesInformation(dataLocal);
|
|
18240
|
+
|
|
18241
|
+
this.doOnIncomingMessage(dataRestored, clientSocketParam);
|
|
17485
18242
|
}
|
|
17486
18243
|
|
|
17487
18244
|
}
|
|
@@ -17529,13 +18286,10 @@ class NodeServerInstance{
|
|
|
17529
18286
|
});
|
|
17530
18287
|
}
|
|
17531
18288
|
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
18289
|
// TODO : DEVELOP...
|
|
17535
18290
|
//sendChainable(channelNameParam, data, clientsRoomsTag=null){
|
|
17536
18291
|
//}
|
|
17537
18292
|
|
|
17538
|
-
|
|
17539
18293
|
receive(channelNameParam, doOnIncomingMessage, listenerConfig=null, clientsRoomsTag=null){
|
|
17540
18294
|
|
|
17541
18295
|
// DBG
|
|
@@ -17583,14 +18337,16 @@ class NodeServerInstance{
|
|
|
17583
18337
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17584
18338
|
|
|
17585
18339
|
// Channel information is stored in exchanged data :
|
|
17586
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
17587
|
-
|
|
17588
|
-
|
|
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!)
|
|
17589
18345
|
dataWrapped=stringifyObject(dataWrapped);
|
|
17590
18346
|
|
|
17591
18347
|
// TODO : FIXME : Use one single interface !
|
|
17592
18348
|
if(!WebsocketImplementation.useSocketIOImplementation) clientSocket.send(dataWrapped);
|
|
17593
|
-
else clientSocket.emit(channelName,dataWrapped);
|
|
18349
|
+
else clientSocket.emit(channelName, dataWrapped);
|
|
17594
18350
|
|
|
17595
18351
|
});
|
|
17596
18352
|
|
|
@@ -17610,12 +18366,12 @@ class NodeServerInstance{
|
|
|
17610
18366
|
if(!WebsocketImplementation.isInRoom(clientSocket,clientsRoomsTag)) return;
|
|
17611
18367
|
|
|
17612
18368
|
// Channel information is stored in exchanged data :
|
|
17613
|
-
let dataWrapped={channelName:channelName, data:data};
|
|
17614
|
-
dataWrapped=stringifyObject(dataWrapped);
|
|
18369
|
+
let dataWrapped=saveClassesInformation({channelName:channelName, data:data});
|
|
17615
18370
|
|
|
17616
|
-
|
|
17617
|
-
//
|
|
17618
|
-
|
|
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);
|
|
17619
18375
|
|
|
17620
18376
|
|
|
17621
18377
|
// TODO : FIXME : Use one single interface !
|
|
@@ -17628,8 +18384,8 @@ class NodeServerInstance{
|
|
|
17628
18384
|
return this;
|
|
17629
18385
|
}
|
|
17630
18386
|
|
|
17631
|
-
|
|
17632
|
-
onConnectionToClient(doOnConnection){
|
|
18387
|
+
|
|
18388
|
+
onConnectionToClient(doOnConnection, doOnConnectionLost=null){
|
|
17633
18389
|
const self=this;
|
|
17634
18390
|
|
|
17635
18391
|
|
|
@@ -17638,7 +18394,6 @@ class NodeServerInstance{
|
|
|
17638
18394
|
|
|
17639
18395
|
// DBG
|
|
17640
18396
|
console.log("SERVER : ON CONNECTION !");
|
|
17641
|
-
|
|
17642
18397
|
|
|
17643
18398
|
const clientId="autogeneratedid_"+getUUID();
|
|
17644
18399
|
|
|
@@ -17689,6 +18444,9 @@ class NodeServerInstance{
|
|
|
17689
18444
|
|
|
17690
18445
|
|
|
17691
18446
|
clearInterval(clientSocket.stateCheckInterval);
|
|
18447
|
+
|
|
18448
|
+
if(doOnConnectionLost) doOnConnectionLost(clientSocket.clientId);
|
|
18449
|
+
|
|
17692
18450
|
return;
|
|
17693
18451
|
}
|
|
17694
18452
|
|
|
@@ -17747,7 +18505,71 @@ class NodeServerInstance{
|
|
|
17747
18505
|
|
|
17748
18506
|
|
|
17749
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)) );
|
|
17750
18570
|
|
|
18571
|
+
return obj;
|
|
18572
|
+
};
|
|
17751
18573
|
|
|
17752
18574
|
|
|
17753
18575
|
|