Commit febb13ae authored by Roque's avatar Roque

[WEB-WORKER] [WIP] Drone simulator refactor

- handle game finish
- handle web worker erros
- update flight log
- cleanup
- move all game logic into one file
- get rid of rescue swarm dependency
- simplify json game parameters
parent b9ba1073
/// <reference path="./GameManager.ts" />
var DroneAPI = /** @class */ (function () {
//*************************************************** CONSTRUCTOR **************************************************
function DroneAPI(gameManager, team) {
this._gameManager = gameManager;
this._team = team;
}
Object.defineProperty(DroneAPI.prototype, "team", {
//*************************************************** ACCESSOR *****************************************************
get: function () {
if (this._team == "L")
return this._gameManager.teamLeft;
else if (this._team == "R")
return this._gameManager.teamRight;
},
enumerable: true,
configurable: true
});
//*************************************************** FUNCTIONS ****************************************************
//#region ------------------ Internal
DroneAPI.prototype.internal_sendMsg = function (msg, to) {
var _this = this;
_this._gameManager.delay(function () {
if (to < 0) {
// Send to all drones
_this.team.forEach(function (drone) {
if (drone.infosMesh) {
try {
drone.onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
drone._internal_crash();
}
}
});
}
else {
// Send to specific drone
if (drone.infosMesh) {
try {
_this.team[to].onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
_this.team[to]._internal_crash();
}
}
}
}, GAMEPARAMETERS.latency.communication);
};
//#endregion
//#region ------------------ Accessible from AI
DroneAPI.prototype.log = function (msg) {
console.log("API say : " + msg);
};
DroneAPI.prototype.getGameParameter = function (name) {
if (["gameTime", "mapSize", "teamSize", "derive", "meteo", "initialHumanAreaPosition"].includes(name))
return this._gameManager.gameParameter[name];
};
DroneAPI.prototype._isWithinDroneView = function (drone_position, element_position) {
// Check if element is under the drone cone-view
var angle = GAMEPARAMETERS.drone.viewAngle ? GAMEPARAMETERS.drone.viewAngle : 60,
radius = drone_position.z * Math.tan(angle/2 * Math.PI/180),
distance = (drone_position.x - element_position.x) * (drone_position.x - element_position.x) +
(drone_position.y - element_position.y) * (drone_position.y - element_position.y);
if (distance < (radius*radius))
return true;
return false;
};
DroneAPI.prototype._getProbabilityOfDetection = function (drone_position) {
var h = drone_position.z,
km = GAMEPARAMETERS.meteo;
prob = 20 * (1 + (110-h)/25) * km;
return prob;
};
DroneAPI.prototype.isHumanPositionSpottedCalculation = function (drone) {
var context = this,
result = false,
drone_position = drone.infosMesh.position;
//swap axes back
drone_position = {
x: drone_position.x,
y: drone_position.z,
z: drone_position.y
};
context._gameManager.teamRight.forEach(function (human) {
if (human.infosMesh && context._isWithinDroneView(drone_position, human.position)) {
var prob = context._getProbabilityOfDetection(drone_position),
random = Math.floor(Math.random()*101);
if (random < prob)
result = true;
}
});
return result;
};
DroneAPI.prototype.isHumanPositionSpotted = function (drone) {
var context = this,
human_detected;
if (drone.__is_calculating_human_position !== true) {
drone.__is_calculating_human_position = true;
//human detection is done with the info captured by the drone
//at the moment this method is called
human_detected = context.isHumanPositionSpottedCalculation(drone);
context._gameManager.delay(function () {
drone.__is_calculating_human_position = false;
try {
drone.onCapture(human_detected);
} catch (error) {
console.warn('Drone crashed on capture due to error:', error);
drone._internal_crash();
}
}, 2000);
}
};
DroneAPI.prototype.getDirectionFromCoordinates = function (x, y, z, drone_position) {
if(isNaN(x) || isNaN(y) || isNaN(z)){
throw new Error('Target coordinates must be numbers');
}
x -= drone_position.x;
y -= drone_position.y;
z -= drone_position.z;
if (this._team == "R")
y = -y;
return {
x: x,
y: y,
z: z
};
};
DroneAPI.prototype.setAltitude = function (altitude) {
//TODO
return;
};
DroneAPI.prototype.getInitialAltitude = function () {
return 0;
};
DroneAPI.prototype.getAltitudeAbs = function () {
return 0;
};
DroneAPI.prototype.getMinHeight = function () {
return 9;
};
DroneAPI.prototype.getMaxHeight = function () {
return 220;
};
DroneAPI.prototype.getDroneAI = function () {
return null;
};
DroneAPI.prototype.getMaxSpeed = function () {
return GAMEPARAMETERS.drone.maxSpeed;
};
return DroneAPI;
}());
......@@ -61,14 +61,12 @@ var GameManager = /** @class */ (function () {
GameManager.prototype.run = function () {
var gadget = this;
return gadget._init();
};
GameManager.prototype.event = function (event) {
var _this = this;
//TODO
console.log("[GM] Event. this._camera:", this._camera);
console.log("[GM] Event. event:", event);
return gadget._init()
.push(function () {
//TODO return result
gadget._final_score = "fake-result 000";
return gadget._final_score;
});
};
GameManager.prototype.update = function () {
......@@ -90,7 +88,11 @@ var GameManager = /** @class */ (function () {
_this.ongoing_update_promise = null;
triggerUpdateIfPossible();
})
.push(undefined, _this.finish_deferred.reject.bind(_this.finish_deferred));
.push(undefined, function(error) {
console.log("ERROR on update:", error);
console.log("rejecting finish_deferred promise...");
_this.finish_deferred.reject.bind(_this.finish_deferred);
});
}
}
triggerUpdateIfPossible();
......@@ -202,12 +204,23 @@ var GameManager = /** @class */ (function () {
});
return queue
//TODO finish
/*.push(function () {
if (_this._allDroneAreOut()) {
.push(function () {
if (_this._timeOut()) {
console.log("TIMEOUT!");
return _this._finish();
}
})*/;
});
};
GameManager.prototype._timeOut = function () {
var seconds = Math.floor(this._game_duration / 1000);
return this._totalTime - seconds <= 0;
};
GameManager.prototype._finish = function () {
console.log("Simulation finished");
this._canUpdate = false;
return this.finish_deferred.resolve();
};
GameManager.prototype._dispose = function () {
......@@ -392,7 +405,6 @@ var GameManager = /** @class */ (function () {
// Timing
this._game_duration = 0;
this._totalTime = GAMEPARAMETERS.gameTime;
this._canUpdate = true;
return new RSVP.Queue()
.push(function () {
......@@ -408,34 +420,7 @@ var GameManager = /** @class */ (function () {
return RSVP.all(promise_list);
})
.push(function () {
//The loop is handle from the outside (webworker)
//_this._scene.registerBeforeRender(function () { console.log("loop"); });
/*_this._scene.registerBeforeRender(function () {
// To increase the game speed, increase this value
_this._max_step_animation_frame = 10;
// time delta means that drone are updated every virtual second
// This is fixed and must not be modified
// otherwise, it will lead to different scenario results
// (as drone calculations may be triggered less often)
var TIME_DELTA = 1000 / 60, i;
// init the value on the first step
_this.waiting_update_count = _this._max_step_animation_frame;
function triggerUpdateIfPossible() {
if ((_this._canUpdate) && (_this.ongoing_update_promise === null) && (0 < _this.waiting_update_count)) {
_this.ongoing_update_promise = _this._update(TIME_DELTA, (_this.waiting_update_count === 1))
.push(function () {
_this.waiting_update_count -= 1;
_this.ongoing_update_promise = null;
triggerUpdateIfPossible();
})
.push(undefined, _this.finish_deferred.reject.bind(_this.finish_deferred));
}
}
triggerUpdateIfPossible();
});*/
//TODO solving promise so game-start finishes and web worker can update
_this.finish_deferred.resolve();
_this._canUpdate = true;
return _this.finish_deferred.promise;
});
};
......
......@@ -10,11 +10,8 @@
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
......@@ -52,11 +49,8 @@
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
......@@ -169,7 +163,7 @@
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
<value> <string>hide</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
......@@ -195,7 +189,7 @@
</tuple>
<state>
<tuple>
<float>1662555757.05</float>
<float>1664290777.68</float>
<string>UTC</string>
</tuple>
</state>
......@@ -204,7 +198,7 @@
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
<value> <string>hidden</string> </value>
</item>
</dictionary>
</list>
......@@ -244,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1003.14332.42567.56439</string> </value>
<value> <string>1003.15811.41174.17766</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1664202073.56</float>
<float>1664291563.2</float>
<string>UTC</string>
</tuple>
</state>
......
/// <reference path="./GameManager.ts" />
var DroneLogAPI = /** @class */ (function () {
//*************************************************** CONSTRUCTOR **************************************************
function DroneLogAPI(gameManager, team, flight_parameters) {
this._gameManager = gameManager;
this._team = team;
this._flight_parameters = flight_parameters;
}
Object.defineProperty(DroneLogAPI.prototype, "team", {
//*************************************************** ACCESSOR *****************************************************
get: function () {
if (this._team == "L")
return this._gameManager.teamLeft;
else if (this._team == "R")
return this._gameManager.teamRight;
},
enumerable: true,
configurable: true
});
//*************************************************** FUNCTIONS ****************************************************
//#region ------------------ Internal
DroneLogAPI.prototype.internal_sendMsg = function (msg, to) {
var _this = this;
_this._gameManager.delay(function () {
if (to < 0) {
// Send to all drones
_this.team.forEach(function (drone) {
if (drone.infosMesh) {
try {
drone.onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
drone._internal_crash();
}
}
});
}
else {
// Send to specific drone
if (drone.infosMesh) {
try {
_this.team[to].onGetMsg(msg);
}
catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
_this.team[to]._internal_crash();
}
}
}
}, GAMEPARAMETERS.latency.communication);
};
//#endregion
//#region ------------------ Accessible from AI
DroneLogAPI.prototype.log = function (msg) {
console.log("API say : " + msg);
};
DroneLogAPI.prototype.getGameParameter = function (name) {
if (["gameTime", "mapSize", "teamSize", "derive", "meteo", "initialHumanAreaPosition"].includes(name))
return this._gameManager.gameParameter[name];
};
DroneLogAPI.prototype._isWithinDroneView = function (drone_position, element_position) {
// Check if element is under the drone cone-view
var angle = GAMEPARAMETERS.drone.viewAngle ? GAMEPARAMETERS.drone.viewAngle : 60,
radius = drone_position.z * Math.tan(angle/2 * Math.PI/180),
distance = (drone_position.x - element_position.x) * (drone_position.x - element_position.x) +
(drone_position.y - element_position.y) * (drone_position.y - element_position.y);
if (distance < (radius*radius))
return true;
return false;
};
DroneLogAPI.prototype._getProbabilityOfDetection = function (drone_position) {
var h = drone_position.z,
km = GAMEPARAMETERS.meteo;
prob = 20 * (1 + (110-h)/25) * km;
return prob;
};
DroneLogAPI.prototype.isHumanPositionSpottedCalculation = function (drone) {
var context = this,
result = false,
drone_position = drone.infosMesh.position;
//swap axes back
drone_position = {
x: drone_position.x,
y: drone_position.z,
z: drone_position.y
};
context._gameManager.teamRight.forEach(function (human) {
if (human.infosMesh && context._isWithinDroneView(drone_position, human.position)) {
var prob = context._getProbabilityOfDetection(drone_position),
random = Math.floor(Math.random()*101);
if (random < prob)
result = true;
}
});
return result;
};
DroneLogAPI.prototype.isHumanPositionSpotted = function (drone) {
var context = this,
human_detected;
if (drone.__is_calculating_human_position !== true) {
drone.__is_calculating_human_position = true;
//human detection is done with the info captured by the drone
//at the moment this method is called
human_detected = context.isHumanPositionSpottedCalculation(drone);
context._gameManager.delay(function () {
drone.__is_calculating_human_position = false;
try {
drone.onCapture(human_detected);
} catch (error) {
console.warn('Drone crashed on capture due to error:', error);
drone._internal_crash();
}
}, 2000);
}
};
DroneLogAPI.prototype.processCoordinates = function (x, y, z) {
if(isNaN(x) || isNaN(y) || isNaN(z)){
throw new Error('Target coordinates must be numbers');
}
return {
x: x,
y: y,
z: z
};
};
DroneLogAPI.prototype.getDroneAI = function () {
return 'function distance(p1, p2) {' +
'var a = p1[0] - p2[0],' +
'b = p1[1] - p2[1];' +
'return Math.sqrt(a * a + b * b);' +
'}' +
'me.onStart = function() {' +
'console.log("DRONE LOG START!");' +
'if (!me.getFlightParameters())' +
'throw "DroneLog API must implement getFlightParameters";' +
'me.flightParameters = me.getFlightParameters();' +
'me.checkpoint_list = me.flightParameters.converted_log_point_list;' +
'me.startTime = new Date();' +
'me.initTimestamp = me.flightParameters.converted_log_point_list[0][3];' +
'me.setTargetCoordinates(me.checkpoint_list[0][0], me.checkpoint_list[0][1], me.checkpoint_list[0][2]);' +
'me.last_checkpoint_reached = -1;' +
'me.setAcceleration(10);' +
'};' +
'me.onUpdate = function () {' +
'var next_checkpoint = me.checkpoint_list[me.last_checkpoint_reached+1];' +
'if (distance([me.position.x, me.position.y], next_checkpoint) < 12) {' +
'var log_elapsed = next_checkpoint[3] - me.initTimestamp,' +
'time_elapsed = new Date() - me.startTime;' +
'if (time_elapsed < log_elapsed) {' +
'me.setDirection(0, 0, 0);' +
'return;' +
'}' +
'if (me.last_checkpoint_reached + 1 === me.checkpoint_list.length - 1) {' +
'me.setTargetCoordinates(me.position.x, me.position.y, me.position.z);' +
'return;' +
'}' +
'me.last_checkpoint_reached += 1;' +
'next_checkpoint = me.checkpoint_list[me.last_checkpoint_reached+1];' +
'me.setTargetCoordinates(next_checkpoint[0], next_checkpoint[1], next_checkpoint[2]);' +
'} else {' +
'me.setTargetCoordinates(next_checkpoint[0], next_checkpoint[1], next_checkpoint[2]);' +
'}' +
'};';
};
DroneLogAPI.prototype.setAltitude = function (altitude) {
return altitude;
};
DroneLogAPI.prototype.getMaxSpeed = function () {
return 3000;
};
DroneLogAPI.prototype.getInitialAltitude = function () {
return 0;
};
DroneLogAPI.prototype.getAltitudeAbs = function () {
return 0;
};
DroneLogAPI.prototype.getMinHeight = function () {
return 0;
};
DroneLogAPI.prototype.getMaxHeight = function () {
return 220;
};
DroneLogAPI.prototype.getFlightParameters = function () {
return this._flight_parameters;
};
return DroneLogAPI;
}());
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1003.8464.37017.2594</string> </value>
<value> <string>1003.18711.10168.13175</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1663849971.49</float>
<float>1664470272.38</float>
<string>UTC</string>
</tuple>
</state>
......
/// <reference path="./typings/babylon.3.1.d.ts" />
var MapManager = /** @class */ (function () {
//*************************************************** CONSTRUCTOR **************************************************
function MapManager(scene) {
var _this = this;
var max = GAMEPARAMETERS.mapSize.width;
if (GAMEPARAMETERS.mapSize.depth > max)
max = GAMEPARAMETERS.mapSize.depth;
if (GAMEPARAMETERS.mapSize.height > max)
max = GAMEPARAMETERS.mapSize.height;
max = max < GAMEPARAMETERS.mapSize.depth ? GAMEPARAMETERS.mapSize.depth : max;
// Skybox
var max_sky = (max * 10 < 20000) ? max * 10 : 20000,
skybox = BABYLON.Mesh.CreateBox("skyBox", max_sky, scene);
skybox.infiniteDistance = true;
skybox.renderingGroupId = 0;
var skyboxMat = new BABYLON.StandardMaterial("skybox", scene);
skyboxMat.backFaceCulling = false;
skyboxMat.disableLighting = true;
skyboxMat.reflectionTexture = new BABYLON.CubeTexture("./assets/skybox/sky", scene);
skyboxMat.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skyboxMat.infiniteDistance = true;
skybox.material = skyboxMat;
// Plane from bottom
var largeGroundMat = new BABYLON.StandardMaterial("largeGroundMat", scene);
largeGroundMat.specularColor = BABYLON.Color3.Black();
largeGroundMat.alpha = 0.4;
var largeGroundBottom = BABYLON.Mesh.CreatePlane("largeGroundBottom", max * 11, scene);
largeGroundBottom.position.y = -0.01;
largeGroundBottom.rotation.x = -Math.PI / 2;
largeGroundBottom.rotation.y = Math.PI;
largeGroundBottom.material = largeGroundMat;
// Camera
scene.activeCamera.upperRadiusLimit = max * 4;
// Terrain
var width = GAMEPARAMETERS.mapSize.width,
depth = GAMEPARAMETERS.mapSize.depth,
height = GAMEPARAMETERS.mapSize.height,
terrain = scene.getMeshByName("terrain001");
terrain.isVisible = true;
terrain.position = BABYLON.Vector3.Zero();
terrain.scaling = new BABYLON.Vector3(depth / 50000, depth / 50000, width / 50000);
// Goals
this._rGoal = BABYLON.Mesh.CreateSphere("rightGoal", 32, GAMEPARAMETERS.goalDiameter, scene);
var rGoalMat = new BABYLON.StandardMaterial("rGoalMat", scene);
rGoalMat.alpha = 0.0;
rGoalMat.diffuseColor = BABYLON.Color3.Red();
this._rGoal.material = rGoalMat;
this._rGoal.position = new BABYLON.Vector3(GAMEPARAMETERS.goalPositionRightTeam.x, GAMEPARAMETERS.goalPositionRightTeam.y, GAMEPARAMETERS.goalPositionRightTeam.z);
this._rGoal.computeWorldMatrix(true);
this._lGoal = BABYLON.Mesh.CreateSphere("leftGoal", 32, GAMEPARAMETERS.goalDiameter, scene);
goal_x = GAMEPARAMETERS.goalPositionLeftTeam.x;
goal_y = GAMEPARAMETERS.goalPositionLeftTeam.y;
goal_z = GAMEPARAMETERS.goalPositionLeftTeam.z;
this._lGoal.position = new BABYLON.Vector3(goal_x, goal_y, goal_z);
var lGoalMat = new BABYLON.StandardMaterial("lGoalMat", scene);
lGoalMat.alpha = 0.0;
lGoalMat.diffuseColor = BABYLON.Color3.Blue();
this._lGoal.material = lGoalMat;
this._lGoal.computeWorldMatrix(true);
//base is now a boat (special object)
ObstacleManager.Prefab.rotation = new BABYLON.Vector3(20.4, 0, 0);
ObstacleManager.Prefab.scaling = new BABYLON.Vector3(15, 15, 15);
goalPart1 = new ObstacleManager("goal_1", scene);
goalPart1.setStartingPosition(goal_x, goal_y, goal_z);
goalPart2 = BABYLON.MeshBuilder.CreateBox("goal_2", { 'size': 1 }, scene);
goalPart2.position = new BABYLON.Vector3(goal_x - 0.5, goal_y + 1.5, goal_z + 1.5);
goalPart2.rotation = new BABYLON.Vector3(0, 0, 0);
goalPart2.scaling = new BABYLON.Vector3(2, 2, 1.5);
goalPart3 = BABYLON.MeshBuilder.CreateCylinder("goal_3", {
'diameterBottom': 1.5,
'diameterTop': 1.5,
'height': 1
}, scene);
goalPart3.position = new BABYLON.Vector3(goal_x + 2.5, goal_y + 1.5, goal_z + 1.5);
goalPart3.rotation = new BABYLON.Vector3(0, 0, 0);
goalPart3.scaling = new BABYLON.Vector3(1.5, 3.5, 1.5);
// Obstacles
var count = 0;
this._obstacles = [];
GAMEPARAMETERS.obstacles.forEach(function (obs) {
var newObj;
switch (obs.type) {
case "box":
newObj = BABYLON.MeshBuilder.CreateBox("obs_" + count, { 'size': 1 }, scene);
break;
case "cylinder":
newObj = BABYLON.MeshBuilder.CreateCylinder("obs_" + count, {
'diameterBottom': obs.diameterBottom,
'diameterTop': obs.diameterTop,
'height': 1
}, scene);
break;
case "sphere":
newObj = BABYLON.MeshBuilder.CreateSphere("obs_" + count, {
'diameterX': obs.scale.x,
'diameterY': obs.scale.y,
'diameterZ': obs.scale.z
}, scene);
break;
case "boat":
ObstacleManager.Prefab.rotation = new BABYLON.Vector3(obs.rotation.x, obs.rotation.y, obs.rotation.z);
ObstacleManager.Prefab.scaling = new BABYLON.Vector3(obs.scale.x * 2, obs.scale.y * 2, obs.scale.z * 2);
newObj = new ObstacleManager("obs_" + count, scene);
newObj.setStartingPosition(obs.position.x, obs.position.y, obs.position.z);
break;
default:
return;
}
newObj["obsType"] = obs.type;
var convertion = Math.PI / 180;
if ("position" in obs)
newObj.position = new BABYLON.Vector3(obs.position.x, obs.position.y, obs.position.z);
if ("rotation" in obs)
newObj.rotation = new BABYLON.Vector3(obs.rotation.x * convertion, obs.rotation.y * convertion, obs.rotation.z * convertion);
if ("scale" in obs)
newObj.scaling = new BABYLON.Vector3(obs.scale.x, obs.scale.y, obs.scale.z);
if ("color" in obs) {
var material = new BABYLON.StandardMaterial(scene);
material.alpha = 1;
material.diffuseColor = new BABYLON.Color3(obs.color.r, obs.color.g, obs.color.b);
newObj.material = material;
}
_this._obstacles.push(newObj);
});
}
Object.defineProperty(MapManager.prototype, "lGoal", {
get: function () { return this._lGoal; },
enumerable: true,
configurable: true
});
Object.defineProperty(MapManager.prototype, "rGoal", {
get: function () { return this._rGoal; },
enumerable: true,
configurable: true
});
Object.defineProperty(MapManager.prototype, "obstacles", {
get: function () { return this._obstacles; },
enumerable: true,
configurable: true
});
return MapManager;
}());
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment