Browse Source

init state machine, init game data in store, first two states init & deck

jojo 4 years ago
parent
commit
48854fe23c
51 changed files with 3839 additions and 345 deletions
  1. 2 0
      .env.development
  2. 0 39
      .vscode/settings.json
  3. 1 0
      package.json
  4. 49 0
      project.code-workspace
  5. 9 2
      server/.babelrc
  6. 46 0
      server/.vscode/launch.json
  7. 170 0
      server/dist/client-server-shared/duel-controller.js
  8. 0 0
      server/dist/client-server-shared/duel-controller.js.map
  9. 71 0
      server/dist/client-server-shared/heroesHelper.js
  10. 0 0
      server/dist/client-server-shared/heroesHelper.js.map
  11. 615 0
      server/dist/db/mariadb-connector.js
  12. 0 0
      server/dist/db/mariadb-connector.js.map
  13. 52 0
      server/dist/game-server/games-manager.js
  14. 0 0
      server/dist/game-server/games-manager.js.map
  15. 107 0
      server/dist/game-server/online-duel-sync.js
  16. 0 0
      server/dist/game-server/online-duel-sync.js.map
  17. 59 0
      server/dist/players/player-id.js
  18. 1 0
      server/dist/players/player-id.js.map
  19. 639 0
      server/dist/server.js
  20. 0 0
      server/dist/server.js.map
  21. 193 0
      server/dist/tools/server-tool-listener.js
  22. 0 0
      server/dist/tools/server-tool-listener.js.map
  23. 68 0
      server/dist/tools/server-tools.js
  24. 0 0
      server/dist/tools/server-tools.js.map
  25. 0 97
      server/game-server/online-duel-sync.js
  26. 868 17
      server/package-lock.json
  27. 6 2
      server/package.json
  28. 1 1
      server/src/client-server-shared/all-heroes.json
  29. 149 0
      server/src/client-server-shared/duel-controller.js
  30. 50 0
      server/src/client-server-shared/gameStates/0-initState.js
  31. 40 0
      server/src/client-server-shared/gameStates/1-selectFactionState.js
  32. 16 0
      server/src/client-server-shared/gameStates/gameStates.js
  33. 64 0
      server/src/client-server-shared/heroesHelper.js
  34. 41 10
      server/src/db/mariadb-connector.js
  35. 32 4
      server/src/game-server/games-manager.js
  36. 187 0
      server/src/game-server/online-duel-sync.js
  37. 0 0
      server/src/players/player-id.js
  38. 13 18
      server/src/server.js
  39. 12 12
      server/src/tools/server-tool-listener.js
  40. 15 16
      server/src/tools/server-tools.js
  41. 10 2
      src/common/heroes-display/HeroesDisplay.vue
  42. 18 0
      src/common/socket-service.js
  43. 2 9
      src/game/AppGame.vue
  44. 1 1
      src/menu/game-creation/MenuGameCreation.vue
  45. 0 28
      src/server-shared/heroesHelper.js
  46. 132 0
      src/store/game/game.js
  47. 44 0
      src/store/game/player/any-player.js
  48. 0 85
      src/store/heroes/heroes.js
  49. 28 0
      src/store/menu/menu.js
  50. 5 2
      src/store/store.js
  51. 23 0
      src/store/types.js

+ 2 - 0
.env.development

@@ -1,4 +1,6 @@
 BROWSER=firefox
+BABEL_ENV=debug
+NODE_ENV=development
 
 //    1. Game server
 

+ 0 - 39
.vscode/settings.json

@@ -1,39 +0,0 @@
-{
-    "javascript.implicitProjectConfig.checkJs": true,
-    "terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\wsl.exe",
-    "javascript.updateImportsOnFileMove.enabled": "always",
-    "typescript.validate.enable": false,
-    "eslint.alwaysShowStatus": true,
-    "eslint.workingDirectories": [
-        "./"
-    ],
-    "javascript.validate.enable": false,
-    "window.zoomLevel": 0,
-    "[javascript]": {
-        "editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
-    },
-    "[vue]": {
-    
-        "editor.defaultFormatter": "octref.vetur"
-    },
-    "emmet.showAbbreviationSuggestions": true,
-    "emmet.showExpandedAbbreviation": "always",
-    "emmet.syntaxProfiles": {
-        "vue-html": "html",
-        "vue":"html"
-    },
-    "vetur.format.defaultFormatter.html": "js-beautify-html",
-    "vetur.format.defaultFormatter.js": "prettier-eslint",
-    "vue-format.format_need": [
-        "html",
-        "js",
-        "css"
-    ],
-    "editor.codeActionsOnSave": {
-        "source.fixAll.eslint": true
-    },
-    "editor.suggestSelection": "first",
-    "emmet.excludeLanguages": [
-        "markdown"
-    ]
-}

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "dev": "vue-cli-service serve --open",
     "server": "cd server && npm start",
     "server-dev": "cd server && npm run server-dev",
+    "debug" : "cd server && npm run debug",
     "tu": "vue-cli-service test:unit --watch",
     "show":"ssh jojo@149.91.81.94 -p 1988 -nNT -o ServerAliveInterval=30 -R 8085:localhost:8080"
   },

+ 49 - 0
project.code-workspace

@@ -0,0 +1,49 @@
+{
+	"folders": [
+		{
+			"path": "."
+		},
+		{
+			"path": "server"
+		}
+	],
+	"settings": {
+		"javascript.implicitProjectConfig.checkJs": true,
+		"terminal.integrated.shell.windows": "C:\\WINDOWS\\System32\\wsl.exe",
+		"javascript.updateImportsOnFileMove.enabled": "always",
+		"typescript.validate.enable": false,
+		"eslint.alwaysShowStatus": true,
+		"eslint.workingDirectories": [
+			"./"
+		],
+		"javascript.validate.enable": false,
+		"window.zoomLevel": -1,
+		"[javascript]": {
+			"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
+		},
+		"[vue]": {
+		
+			"editor.defaultFormatter": "octref.vetur"
+		},
+		"emmet.showAbbreviationSuggestions": true,
+		"emmet.showExpandedAbbreviation": "always",
+		"emmet.syntaxProfiles": {
+			"vue-html": "html",
+			"vue":"html"
+		},
+		"vetur.format.defaultFormatter.html": "js-beautify-html",
+		"vetur.format.defaultFormatter.js": "prettier-eslint",
+		"vue-format.format_need": [
+			"html",
+			"js",
+			"css"
+		],
+		"editor.codeActionsOnSave": {
+			"source.fixAll.eslint": true
+		},
+		"editor.suggestSelection": "first",
+		"emmet.excludeLanguages": [
+			"markdown"
+		]
+	}
+}

+ 9 - 2
server/.babelrc

@@ -1,7 +1,14 @@
 {
   "presets": ["@babel/preset-env"],
   "plugins": [
-    "@babel/plugin-proposal-object-rest-spread"
-  ]
+    "@babel/plugin-proposal-object-rest-spread",
+    "@babel/plugin-transform-runtime"
+  ],
+  "env": {
+    "debug": {
+      "sourceMaps": "inline",
+      "retainLines": true
+    }
+  }
 }
 

+ 46 - 0
server/.vscode/launch.json

@@ -0,0 +1,46 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch",
+            "type": "node",
+            "request": "launch",
+            "program": "${workspaceRoot}/src/server.js",
+            "stopOnEntry": false,
+            "args": [],
+            "cwd": "${workspaceRoot}",
+            "preLaunchTask": null,
+            "runtimeExecutable": null,
+            "runtimeArgs": [
+                "--nolazy",
+                "--require",
+                "@babel/register"
+            ],
+            "envFile": "${workspaceFolder}/../.env.development",
+            "console": "internalConsole",
+            "sourceMaps": true,
+            "outFiles": []
+        },
+        {
+            "name": "Attach",
+            "type": "node",
+            "request": "attach",
+            "port": 5858,
+            "address": "localhost",
+            "restart": false,
+            "sourceMaps": false,
+            "outFiles": [],
+            "localRoot": "${workspaceRoot}",
+            "remoteRoot": null
+        },
+        {
+            "name": "Attach to Process",
+            "type": "node",
+            "request": "attach",
+            "processId": "${command.PickProcess}",
+            "port": 5858,
+            "sourceMaps": false,
+            "outFiles": []
+        }
+    ]
+}

+ 170 - 0
server/dist/client-server-shared/duel-controller.js

@@ -0,0 +1,170 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+var _heroesHelper = require("./heroesHelper");
+
+var DuelController = function DuelController(dataStore) {
+  var currentGameState = null;
+  var gameData = {};
+  var allHeroesSet = {};
+  var states = {
+    '0_INIT': new initState(this),
+    '1_SELECT_FACTION': new selectFactionState(this)
+  };
+
+  var setCurrentState = function setCurrentState(stateName) {
+    var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
+    currentGameState = states[stateName];
+    gameData.gameState = stateName;
+    currentGameState.start(payload);
+  };
+
+  var updateCurrentState = function updateCurrentState() {
+    var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+    currentGameState.update(payload);
+  };
+
+  var storeData = function storeData() {
+    return dataStore.save(gameData);
+  };
+
+  var loadData = function loadData() {
+    gameData = dataStore.load();
+  };
+
+  var startNewGame = function startNewGame(player1Name, player2Name, allHeroesJson, gameOptions) {
+    allHeroesSet = (0, _heroesHelper.initHeroesFromJson)(allHeroesJson);
+    setCurrentState('0_INIT', {
+      player1Name: player1Name,
+      player2Name: player2Name,
+      gameOptions: gameOptions
+    });
+    updateCurrentState();
+  };
+
+  var resumeGame = function resumeGame() {};
+
+  var DuelControllerProxy = function DuelControllerProxy() {
+    return {
+      endTurn: function endTurn(data) {
+        currentGameState.update(data);
+      }
+    };
+  };
+
+  return {
+    startNewGame: startNewGame,
+    resumeGame: resumeGame,
+    duelControllerProxy: new DuelControllerProxy(),
+    storeData: storeData,
+    loadData: loadData,
+    setCurrentState: setCurrentState,
+    updateCurrentState: updateCurrentState,
+    gameData: gameData,
+    allHeroesSet: allHeroesSet
+  };
+};
+
+var initState = function initState(duelCtrl) {
+  var start = function start() {
+    var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+    duelCtrl.gameData = {
+      playerBlue: {
+        name: payload.player1Name
+      },
+      playerRed: {
+        name: payload.player2Name
+      },
+      gameOptions: payload.gameOptions,
+      waitingForBlue: false,
+      waitingForRed: false,
+      currentPlayer: ''
+    };
+  };
+
+  var update = function update() {
+    var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+    duelCtrl.storeData().then(function () {
+      switch (duelCtrl.gameData.gameOptions.deck) {
+        case 'faction':
+          duelCtrl.setCurrentState('1_SELECT_FACTION');
+          break;
+
+        case 'tournament':
+          break;
+
+        case 'draft':
+          break;
+
+        default:
+          break;
+      }
+    }).catch(function (err) {
+      console.log('Error storing data ', err); // Retry in 5 sec
+
+      setTimeout(function () {
+        duelCtrl.updateCurrentState();
+      }, 5000);
+    });
+  };
+
+  return {
+    start: start,
+    update: update
+  };
+};
+
+var selectFactionState = function selectFactionState(duelCtrl) {
+  var start = function start() {
+    var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+    duelCtrl.gameData.heroesToSelect = duelCtrl.allHeroesSet;
+    duelCtrl.gameData.waitingForBlue = true;
+    duelCtrl.gameData.waitingForRed = true;
+    duelCtrl.gameData.currentPlayer = 'both';
+    duelCtrl.storeData.then(function () {
+      console.log('Wating for both players');
+    }).catch(function (err) {
+      console.log('Error storing data ', err); // Retry in 5 sec
+
+      setTimeout(function () {
+        duelCtrl.currentGameState.start();
+      }, 5000);
+    });
+  };
+
+  var update = function update() {
+    var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+
+    if (payload) {
+      var player = 'player' + payload.player;
+      duelCtrl.gameData[player].faction = payload.faction;
+      duelCtrl.gameData[player].twelveHeroes = (0, _heroesHelper.getRandomHeroesByFaction)(payload.faction);
+      duelCtrl.gameData['waitingFor' + payload.player] = false;
+    }
+
+    if (duelCtrl.gameData.waitingForBlue === false && duelCtrl.gameData.waitingForRed === false) {
+      duelCtrl.storeData.then(function () {
+        console.log('Going to next STATE');
+      }).catch(function (err) {
+        console.log('Error storing data ', err); // Retry in 5 sec
+
+        setTimeout(function () {
+          duelCtrl.currentGameState.update();
+        }, 5000);
+      });
+    }
+  };
+
+  return {
+    start: start,
+    update: update
+  };
+};
+
+var _default = DuelController;
+exports.default = _default;
+//# sourceMappingURL=duel-controller.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/client-server-shared/duel-controller.js.map


+ 71 - 0
server/dist/client-server-shared/heroesHelper.js

@@ -0,0 +1,71 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.getDraftSets = exports.getRandomHeroesByFaction = exports.initHeroesFromJson = void 0;
+
+function shuffle(a) {
+  for (var i = a.length - 1; i > 0; i--) {
+    var j = Math.floor(Math.random() * (i + 1));
+    var _ref = [a[j], a[i]];
+    a[i] = _ref[0];
+    a[j] = _ref[1];
+  }
+
+  return a;
+}
+
+var initHeroesFromJson = function initHeroesFromJson(allHeroesJson) {
+  var abilitiesMap = new Map();
+  allHeroesJson.abilities.forEach(function (ability) {
+    abilitiesMap.set(ability.abilityName, {
+      name: ability.abilityName,
+      hook: ability.abilityHook,
+      isOptionnal: ability.optionnal,
+      desc: ability['abilityDesc-FR']
+    });
+  });
+  var heroesSet = new Set();
+  allHeroesJson.heroes.forEach(function (hero) {
+    var i = 0;
+
+    while (i < hero.nbInDeck) {
+      heroesSet.add({
+        name: hero.name,
+        cost: hero.cost,
+        power: hero.power,
+        faction: hero.faction,
+        ability: abilitiesMap.get(hero.ability),
+        isDraftable: hero.draftMode,
+        popularity: hero.popularity
+      });
+      i++;
+    }
+  });
+  return heroesSet;
+};
+
+exports.initHeroesFromJson = initHeroesFromJson;
+
+var getRandomHeroesByFaction = function getRandomHeroesByFaction(allHeroes, faction) {
+  var factionArray = allHeroes.filter(function (hero) {
+    return hero.faction === faction;
+  });
+  return shuffle(factionArray);
+};
+
+exports.getRandomHeroesByFaction = getRandomHeroesByFaction;
+
+var getDraftSets = function getDraftSets(allHeroes) {
+  var heroesDraftable = allHeroes.filter(function (hero) {
+    return hero.isDraftable === true;
+  });
+  var shuffledHeroes = shuffle(heroesDraftable); // Return two sets of 12 heroes for draft mode
+
+  var draftSets = [shuffledHeroes.slice(0, 11), shuffledHeroes.slice(12, 23)];
+  return draftSets;
+};
+
+exports.getDraftSets = getDraftSets;
+//# sourceMappingURL=heroesHelper.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/client-server-shared/heroesHelper.js.map


+ 615 - 0
server/dist/db/mariadb-connector.js

@@ -0,0 +1,615 @@
+'use strict';
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
+
+var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
+
+var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
+
+var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
+
+var _mariadb = _interopRequireDefault(require("mariadb"));
+
+function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
+
+function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
+
+function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
+
+var JOJOAPPS_SERVER_DB = 'localhost';
+var JOJOAPPS_SERVER_DB_SOCKET = '/run/mysqld/mysqld.sock';
+var DATABASE_NAME = 'twelve_heroes';
+
+var MariadbConnector = /*#__PURE__*/function () {
+  function MariadbConnector() {
+    (0, _classCallCheck2.default)(this, MariadbConnector);
+    var host = process.env.DB || JOJOAPPS_SERVER_DB;
+    var port = process.env.DB_PORT || 0;
+    var database = process.env.DB_NAME || DATABASE_NAME;
+    console.log('DB server : ' + host);
+    console.log('DB database : ' + database);
+    var mariaDbConfig = {
+      host: host,
+      user: 'node',
+      password: 'nodejs1234',
+      database: database,
+      connectionLimit: 5
+    };
+
+    if (port === 0) {
+      mariaDbConfig.socketPath = JOJOAPPS_SERVER_DB_SOCKET;
+      console.log('Will use DB UNIX socket : ' + mariaDbConfig.socketPath);
+    } else {
+      mariaDbConfig.port = port;
+      console.log('Will use DB port : ' + port);
+    }
+
+    this.pool = _mariadb.default.createPool(mariaDbConfig);
+  }
+
+  (0, _createClass2.default)(MariadbConnector, [{
+    key: "getUsernames",
+    value: function () {
+      var _getUsernames = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
+        var conn, players, rows_players, _iterator, _step, u;
+
+        return _regenerator.default.wrap(function _callee$(_context) {
+          while (1) {
+            switch (_context.prev = _context.next) {
+              case 0:
+                players = [];
+                _context.prev = 1;
+                _context.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context.sent;
+                _context.next = 7;
+                return conn.query('SELECT username FROM players');
+
+              case 7:
+                rows_players = _context.sent;
+                _iterator = _createForOfIteratorHelper(rows_players);
+
+                try {
+                  for (_iterator.s(); !(_step = _iterator.n()).done;) {
+                    u = _step.value.username;
+                    players.push(u);
+                  }
+                } catch (err) {
+                  _iterator.e(err);
+                } finally {
+                  _iterator.f();
+                }
+
+              case 10:
+                _context.prev = 10;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context.finish(10);
+
+              case 13:
+                return _context.abrupt("return", players);
+
+              case 14:
+              case "end":
+                return _context.stop();
+            }
+          }
+        }, _callee, this, [[1,, 10, 13]]);
+      }));
+
+      function getUsernames() {
+        return _getUsernames.apply(this, arguments);
+      }
+
+      return getUsernames;
+    }()
+  }, {
+    key: "addPlayer",
+    value: function () {
+      var _addPlayer = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(username) {
+        var conn, val;
+        return _regenerator.default.wrap(function _callee2$(_context2) {
+          while (1) {
+            switch (_context2.prev = _context2.next) {
+              case 0:
+                console.log('mariadb add player : ', username);
+                _context2.prev = 1;
+                _context2.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context2.sent;
+                _context2.next = 7;
+                return conn.query('INSERT INTO players value (?, NOW(),NULL)', [username]);
+
+              case 7:
+                val = _context2.sent;
+                console.log('OK removing player : ', val);
+                return _context2.abrupt("return", val);
+
+              case 12:
+                _context2.prev = 12;
+                _context2.t0 = _context2["catch"](1);
+                console.log('error adding player : ', _context2.t0);
+                throw _context2.t0;
+
+              case 16:
+                _context2.prev = 16;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context2.finish(16);
+
+              case 19:
+              case "end":
+                return _context2.stop();
+            }
+          }
+        }, _callee2, this, [[1, 12, 16, 19]]);
+      }));
+
+      function addPlayer(_x) {
+        return _addPlayer.apply(this, arguments);
+      }
+
+      return addPlayer;
+    }()
+  }, {
+    key: "removePlayer",
+    value: function () {
+      var _removePlayer = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(username) {
+        var conn, val;
+        return _regenerator.default.wrap(function _callee3$(_context3) {
+          while (1) {
+            switch (_context3.prev = _context3.next) {
+              case 0:
+                console.log('mariadb remove player : ', username);
+                _context3.prev = 1;
+                _context3.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context3.sent;
+                _context3.next = 7;
+                return conn.query('DELETE FROM players WHERE username=?', [username]);
+
+              case 7:
+                val = _context3.sent;
+                console.log('OK removing player : ', val);
+                return _context3.abrupt("return", val);
+
+              case 12:
+                _context3.prev = 12;
+                _context3.t0 = _context3["catch"](1);
+                console.log('error removing player : ', _context3.t0);
+                throw _context3.t0;
+
+              case 16:
+                _context3.prev = 16;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context3.finish(16);
+
+              case 19:
+              case "end":
+                return _context3.stop();
+            }
+          }
+        }, _callee3, this, [[1, 12, 16, 19]]);
+      }));
+
+      function removePlayer(_x2) {
+        return _removePlayer.apply(this, arguments);
+      }
+
+      return removePlayer;
+    }()
+  }, {
+    key: "addNewGame",
+    value: function () {
+      var _addNewGame = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(game) {
+        var conn, val;
+        return _regenerator.default.wrap(function _callee4$(_context4) {
+          while (1) {
+            switch (_context4.prev = _context4.next) {
+              case 0:
+                console.log('mariadb add game : ', game);
+                _context4.prev = 1;
+                _context4.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context4.sent;
+                _context4.next = 7;
+                return conn.query('INSERT INTO games values (NULL,?,?,?,?,?,?,NOW())', [game.player1, game.player2, game.deck, convertAdvRulesToString(game.advRules), game.status, game.data]);
+
+              case 7:
+                val = _context4.sent;
+                console.log('OK adding game : ', val);
+                return _context4.abrupt("return", val.insertId);
+
+              case 12:
+                _context4.prev = 12;
+                _context4.t0 = _context4["catch"](1);
+                console.log('error adding game : ', _context4.t0);
+                throw _context4.t0;
+
+              case 16:
+                _context4.prev = 16;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context4.finish(16);
+
+              case 19:
+              case "end":
+                return _context4.stop();
+            }
+          }
+        }, _callee4, this, [[1, 12, 16, 19]]);
+      }));
+
+      function addNewGame(_x3) {
+        return _addNewGame.apply(this, arguments);
+      }
+
+      return addNewGame;
+    }()
+  }, {
+    key: "removeGameById",
+    value: function removeGameById(gameId) {
+      return this.removeGame('DELETE FROM games WHERE id=?', [gameId]);
+    }
+  }, {
+    key: "removeGamesByPlayer1",
+    value: function removeGamesByPlayer1(player1Name) {
+      return this.removeGame('DELETE FROM games WHERE player1=?', [player1Name]);
+    }
+  }, {
+    key: "removeGamesByPlayerAny",
+    value: function removeGamesByPlayerAny(playerName) {
+      return this.removeGame('DELETE FROM games WHERE player1=? OR player2=?', [playerName, playerName]);
+    }
+  }, {
+    key: "removeGamesByStatus",
+    value: function removeGamesByStatus(status) {
+      return this.removeGame('DELETE FROM games WHERE status=?', [status]);
+    }
+  }, {
+    key: "removeGamesByDays",
+    value: function removeGamesByDays(days) {
+      return this.removeGame('DELETE FROM games WHERE  datediff(NOW(),last_played) > ?', [days]);
+    }
+  }, {
+    key: "removeFinishedGamesByDays",
+    value: function removeFinishedGamesByDays(days) {
+      return this.removeGame("DELETE FROM games WHERE status='FINISHED' AND datediff(NOW(),last_played) > ?", [days]);
+    }
+  }, {
+    key: "removeCreatedGamesByPlayer1",
+    value: function removeCreatedGamesByPlayer1(player1Name) {
+      return this.removeGame("DELETE FROM games WHERE player1=? AND status='CREATED'", [player1Name]);
+    }
+  }, {
+    key: "removeGame",
+    value: function () {
+      var _removeGame = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(queryStr, queryArgs) {
+        var conn, val;
+        return _regenerator.default.wrap(function _callee5$(_context5) {
+          while (1) {
+            switch (_context5.prev = _context5.next) {
+              case 0:
+                console.log('mariadb remove game  : ', queryStr);
+                _context5.prev = 1;
+                _context5.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context5.sent;
+                _context5.next = 7;
+                return conn.query(queryStr, queryArgs);
+
+              case 7:
+                val = _context5.sent;
+                console.log('OK removing game : ', val);
+                return _context5.abrupt("return", val);
+
+              case 12:
+                _context5.prev = 12;
+                _context5.t0 = _context5["catch"](1);
+                console.log('error removing game : ', _context5.t0);
+                throw _context5.t0;
+
+              case 16:
+                _context5.prev = 16;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context5.finish(16);
+
+              case 19:
+              case "end":
+                return _context5.stop();
+            }
+          }
+        }, _callee5, this, [[1, 12, 16, 19]]);
+      }));
+
+      function removeGame(_x4, _x5) {
+        return _removeGame.apply(this, arguments);
+      }
+
+      return removeGame;
+    }()
+  }, {
+    key: "getJoinableGamesForPlayer",
+    value: function () {
+      var _getJoinableGamesForPlayer = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(username) {
+        var conn, games, res, _iterator2, _step2, _step2$value, id, player1, player2, deck, adv_rules;
+
+        return _regenerator.default.wrap(function _callee6$(_context6) {
+          while (1) {
+            switch (_context6.prev = _context6.next) {
+              case 0:
+                games = [];
+                _context6.prev = 1;
+                _context6.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context6.sent;
+                _context6.next = 7;
+                return conn.query("SELECT * FROM games WHERE (status = 'CREATED' OR status = 'PAUSED' OR status = 'PLAYING')  AND(player1 = ? OR player2 = '' OR player2 = ?)", [username, username]);
+
+              case 7:
+                res = _context6.sent;
+                _iterator2 = _createForOfIteratorHelper(res);
+
+                try {
+                  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
+                    _step2$value = _step2.value, id = _step2$value.id, player1 = _step2$value.player1, player2 = _step2$value.player2, deck = _step2$value.deck, adv_rules = _step2$value.adv_rules;
+                    games.push({
+                      id: id,
+                      player1: player1,
+                      player2: player2,
+                      deck: deck,
+                      adv_rules: adv_rules
+                    });
+                  }
+                } catch (err) {
+                  _iterator2.e(err);
+                } finally {
+                  _iterator2.f();
+                }
+
+                console.log("returning for ".concat(username, " : "), games);
+
+              case 11:
+                _context6.prev = 11;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context6.finish(11);
+
+              case 14:
+                return _context6.abrupt("return", games);
+
+              case 15:
+              case "end":
+                return _context6.stop();
+            }
+          }
+        }, _callee6, this, [[1,, 11, 14]]);
+      }));
+
+      function getJoinableGamesForPlayer(_x6) {
+        return _getJoinableGamesForPlayer.apply(this, arguments);
+      }
+
+      return getJoinableGamesForPlayer;
+    }()
+  }, {
+    key: "getGameById",
+    value: function () {
+      var _getGameById = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(gameId) {
+        var conn, game;
+        return _regenerator.default.wrap(function _callee7$(_context7) {
+          while (1) {
+            switch (_context7.prev = _context7.next) {
+              case 0:
+                game = {};
+                _context7.prev = 1;
+                _context7.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context7.sent;
+                _context7.next = 7;
+                return conn.query('SELECT * FROM games WHERE id=?', gameId);
+
+              case 7:
+                game = _context7.sent;
+
+              case 8:
+                _context7.prev = 8;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context7.finish(8);
+
+              case 11:
+                return _context7.abrupt("return", game);
+
+              case 12:
+              case "end":
+                return _context7.stop();
+            }
+          }
+        }, _callee7, this, [[1,, 8, 11]]);
+      }));
+
+      function getGameById(_x7) {
+        return _getGameById.apply(this, arguments);
+      }
+
+      return getGameById;
+    }()
+  }, {
+    key: "updateGameStatus",
+    value: function () {
+      var _updateGameStatus = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(id, status) {
+        var player2,
+            conn,
+            result,
+            _args8 = arguments;
+        return _regenerator.default.wrap(function _callee8$(_context8) {
+          while (1) {
+            switch (_context8.prev = _context8.next) {
+              case 0:
+                player2 = _args8.length > 2 && _args8[2] !== undefined ? _args8[2] : null;
+                result = {};
+                _context8.prev = 2;
+                _context8.next = 5;
+                return this.pool.getConnection();
+
+              case 5:
+                conn = _context8.sent;
+
+                if (!player2) {
+                  _context8.next = 12;
+                  break;
+                }
+
+                _context8.next = 9;
+                return conn.query('UPDATE games SET player2=?, status = ?, last_played = NOW() WHERE games.id =?', player2, status, id);
+
+              case 9:
+                result = _context8.sent;
+                _context8.next = 15;
+                break;
+
+              case 12:
+                _context8.next = 14;
+                return conn.query('UPDATE games SET status = ?, last_played = NOW() WHERE games.id =?', status, id);
+
+              case 14:
+                result = _context8.sent;
+
+              case 15:
+                _context8.prev = 15;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context8.finish(15);
+
+              case 18:
+                return _context8.abrupt("return", result);
+
+              case 19:
+              case "end":
+                return _context8.stop();
+            }
+          }
+        }, _callee8, this, [[2,, 15, 18]]);
+      }));
+
+      function updateGameStatus(_x8, _x9) {
+        return _updateGameStatus.apply(this, arguments);
+      }
+
+      return updateGameStatus;
+    }()
+  }, {
+    key: "updateGameData",
+    value: function () {
+      var _updateGameData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(id, data) {
+        var conn, result;
+        return _regenerator.default.wrap(function _callee9$(_context9) {
+          while (1) {
+            switch (_context9.prev = _context9.next) {
+              case 0:
+                result = {};
+                _context9.prev = 1;
+                _context9.next = 4;
+                return this.pool.getConnection();
+
+              case 4:
+                conn = _context9.sent;
+                _context9.next = 7;
+                return conn.query('UPDATE games SET game_data = ?, last_played = NOW() WHERE games.id =?', data, id);
+
+              case 7:
+                result = _context9.sent;
+
+              case 8:
+                _context9.prev = 8;
+
+                if (conn && conn !== null) {
+                  conn.end();
+                }
+
+                return _context9.finish(8);
+
+              case 11:
+                return _context9.abrupt("return", result);
+
+              case 12:
+              case "end":
+                return _context9.stop();
+            }
+          }
+        }, _callee9, this, [[1,, 8, 11]]);
+      }));
+
+      function updateGameData(_x10, _x11) {
+        return _updateGameData.apply(this, arguments);
+      }
+
+      return updateGameData;
+    }()
+  }]);
+  return MariadbConnector;
+}();
+
+exports.default = MariadbConnector;
+
+var convertAdvRulesToString = function convertAdvRulesToString(advRulesArray) {
+  var advRulestStr = '';
+  var last = advRulesArray.length - 1;
+  advRulesArray.forEach(function (rule, index) {
+    advRulestStr += rule;
+
+    if (index < last) {
+      advRulestStr += ',';
+    }
+  });
+  return advRulestStr;
+};
+//# sourceMappingURL=mariadb-connector.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/db/mariadb-connector.js.map


+ 52 - 0
server/dist/game-server/games-manager.js

@@ -0,0 +1,52 @@
+"use strict";
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = GamesManager;
+
+var _onlineDuelSync = _interopRequireDefault(require("./online-duel-sync"));
+
+function GamesManager(ioServer, mariadbConn) {
+  var currentGames = new Map(); // Method to add player into a game, create or add to game syncer
+
+  var addPlayerInGame = function addPlayerInGame(player, gameId, joinCreatedGame) {
+    var isNewGame = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
+    var gameOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+
+    if (currentGames.has(gameId)) {
+      player.isPlayingGameId = gameId;
+      return currentGames.get(gameId).addPlayer(player);
+    } else if (joinCreatedGame === false) {
+      player.isPlayingGameId = gameId;
+      currentGames.set(gameId, new _onlineDuelSync.default(ioServer, mariadbConn, gameId, player, isNewGame, gameOptions));
+      return true; // We are supposed to join a new game, but not there, must have been deleted in the meantime
+    } else {
+      console.log('error : ' + player.playerName + ' tries to join a created game that does not exist anymore');
+      return false;
+    }
+  };
+
+  var playerLeft = function playerLeft(player, disconnected) {
+    var id = player.isPlayingGameId;
+
+    if (currentGames.has(id)) {
+      currentGames.get(id).playerLeft(player, disconnected);
+
+      if (!currentGames.get(id).hasPlayers()) {
+        currentGames.delete(id);
+        mariadbConn.updateGameStatus(id, 'PAUSED');
+      }
+    }
+
+    player.isPlayingGameId = -1;
+  };
+
+  return {
+    addPlayerInGame: addPlayerInGame,
+    playerLeft: playerLeft
+  };
+}
+//# sourceMappingURL=games-manager.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/game-server/games-manager.js.map


+ 107 - 0
server/dist/game-server/online-duel-sync.js

@@ -0,0 +1,107 @@
+"use strict";
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = OnlineDuelSync;
+
+var _duelController = _interopRequireDefault(require("../client-server-shared/duel-controller"));
+
+var fs = require('fs');
+
+function OnlineDuelSync(ioServer, mariadbConnector, gameId, firstPlayer, isNewGame, gameOptions) {
+  var io = ioServer;
+  var mdb = mariadbConnector;
+  var isNew = isNewGame;
+  var players = new Map();
+  var duelController = new _duelController.default();
+  players.set(firstPlayer.playerName, firstPlayer); // If not new game, set status to PLAYING right now
+  // Otherwise we wait second player
+
+  if (!isNew) {
+    mdb.updateGameStatus(gameId, 'PLAYING');
+    duelController.resumeGame();
+  }
+
+  var loadGameData = function loadGameData() {
+    var gameDetails = {};
+
+    try {
+      gameDetails = mdb.getGameById(gameId);
+    } catch (err) {
+      console.log('Error retreiving game : ', err.message);
+    }
+
+    return gameDetails;
+  };
+
+  var chat = function chat(name, message) {
+    io.to(gameId).emit('message', {
+      from: name,
+      text: message
+    });
+  };
+
+  firstPlayer.getSocket().join(gameId, function () {
+    chat(firstPlayer.playerName, firstPlayer.playerName + ' has entered the game, waiting for second player...');
+  }).emit('update-game-data', loadGameData().game_data).on('chat', function (message) {
+    chat(firstPlayer.playerName, message);
+  });
+
+  var addPlayer = function addPlayer(player) {
+    var res = true;
+
+    if (players.size === 1) {
+      if (players.has(player.playerName)) {
+        console.log('ERROR :  this player is already there ! ', player.playerName);
+        return false;
+      }
+
+      players.set(player.playerName, player); // Both players connected to new game, set status to PLAYING
+
+      if (isNew) {
+        isNew = false;
+        mdb.updateGameStatus(gameId, 'PLAYING', player.playerName);
+        var allHeroesJson = JSON.parse(fs.readFileSync('../../src/server-shared/all-heroes.json'));
+        duelController.startNewGame(firstPlayer.playerName, player.playerName, allHeroesJson, gameOptions);
+      }
+
+      player.getSocket().join(gameId, function () {
+        chat(player.playerName, player.playerName + ' has entered the game');
+      }).emit('update-game-data', loadGameData().game_data).on('chat', function (message) {
+        chat(player.playerName, message);
+      });
+    } else {
+      console.log('Already 2 players, weird :' + player.playerName + ' was trying to join : ', players);
+      res = false;
+    }
+
+    return res;
+  };
+
+  var playerLeft = function playerLeft(player, disconnected) {
+    var playerLeaving = players.get(player.playerName);
+
+    if (playerLeaving) {
+      if (!disconnected) {
+        playerLeaving.getSocket().leave(gameId);
+      }
+
+      chat(player.playerName, player.playerName + ' has left the game');
+      players.delete(player.playerName);
+    }
+  };
+
+  var hasPlayers = function hasPlayers() {
+    return players.size > 0;
+  };
+
+  return {
+    addPlayer: addPlayer,
+    playerLeft: playerLeft,
+    hasPlayers: hasPlayers
+  };
+}
+//# sourceMappingURL=online-duel-sync.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/game-server/online-duel-sync.js.map


+ 59 - 0
server/dist/players/player-id.js

@@ -0,0 +1,59 @@
+'use strict';
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
+
+var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
+
+var PlayerId = /*#__PURE__*/function () {
+  function PlayerId() {
+    var playerName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
+    (0, _classCallCheck2.default)(this, PlayerId);
+    this.playerSocket = null;
+    this.playerName = playerName;
+    this.connected = false;
+    this.isPlayingGameId = -1;
+  }
+
+  (0, _createClass2.default)(PlayerId, [{
+    key: "getSocket",
+    value: function getSocket() {
+      return this.playerSocket;
+    }
+  }, {
+    key: "setSocket",
+    value: function setSocket(socket) {
+      this.playerSocket = socket;
+    }
+  }, {
+    key: "setPlayerName",
+    value: function setPlayerName(playerName) {
+      this.playerName = playerName;
+    }
+  }, {
+    key: "setPlayerColor",
+    value: function setPlayerColor(playerColor) {
+      this.playerColor = playerColor;
+    }
+  }, {
+    key: "setConnected",
+    value: function setConnected(connected) {
+      this.connected = connected;
+    }
+  }, {
+    key: "isConnected",
+    value: function isConnected() {
+      return this.connected;
+    }
+  }]);
+  return PlayerId;
+}();
+
+exports.default = PlayerId;
+//# sourceMappingURL=player-id.js.map

+ 1 - 0
server/dist/players/player-id.js.map

@@ -0,0 +1 @@
+{"version":3,"sources":["../../src/players/player-id.js"],"names":["PlayerId","playerName","playerSocket","connected","isPlayingGameId","socket","playerColor"],"mappings":"AAAA;;;;;;;;;;;;;IAEqBA,Q;AACnB,sBAA6B;AAAA,QAAjBC,UAAiB,uEAAJ,EAAI;AAAA;AAC3B,SAAKC,YAAL,GAAoB,IAApB;AACA,SAAKD,UAAL,GAAkBA,UAAlB;AACA,SAAKE,SAAL,GAAiB,KAAjB;AACA,SAAKC,eAAL,GAAuB,CAAC,CAAxB;AACD;;;;gCAEW;AACV,aAAO,KAAKF,YAAZ;AACD;;;8BACSG,M,EAAQ;AAChB,WAAKH,YAAL,GAAoBG,MAApB;AACD;;;kCACaJ,U,EAAY;AACxB,WAAKA,UAAL,GAAkBA,UAAlB;AACD;;;mCACcK,W,EAAa;AAC1B,WAAKA,WAAL,GAAmBA,WAAnB;AACD;;;iCACYH,S,EAAW;AACtB,WAAKA,SAAL,GAAiBA,SAAjB;AACD;;;kCACa;AACZ,aAAO,KAAKA,SAAZ;AACD","sourcesContent":["'use strict';\n\nexport default class PlayerId {\n  constructor(playerName = '') {\n    this.playerSocket = null;\n    this.playerName = playerName;\n    this.connected = false;\n    this.isPlayingGameId = -1;\n  }\n\n  getSocket() {\n    return this.playerSocket;\n  }\n  setSocket(socket) {\n    this.playerSocket = socket;\n  }\n  setPlayerName(playerName) {\n    this.playerName = playerName;\n  }\n  setPlayerColor(playerColor) {\n    this.playerColor = playerColor;\n  }\n  setConnected(connected) {\n    this.connected = connected;\n  }\n  isConnected() {\n    return this.connected;\n  }\n}\n"],"file":"player-id.js"}

+ 639 - 0
server/dist/server.js

@@ -0,0 +1,639 @@
+'use strict';
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
+
+var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
+
+var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
+
+var _playerId = _interopRequireDefault(require("./players/player-id"));
+
+var _gamesManager = _interopRequireDefault(require("./game-server/games-manager"));
+
+var _mariadbConnector = _interopRequireDefault(require("./db/mariadb-connector"));
+
+var _serverToolListener = _interopRequireDefault(require("./tools/server-tool-listener"));
+
+function Server() {
+  console.log('Starting 12 heroes server');
+
+  var io = require('socket.io');
+
+  var server = io.listen(2610);
+  var mariadbConn = new _mariadbConnector.default();
+  var gamesManager = new _gamesManager.default(server, mariadbConn);
+  var authorizedPlayers = new Map();
+  var connectedPlayers = new Map();
+  var authorizedPlayersNames = new Set();
+  server.on('connection', function (socket) {
+    console.log('A player connected with id : ' + socket.id);
+    socket.on('disconnect', function (reason) {
+      console.log('A player disconnected, reason : ' + reason);
+
+      if (reason === 'client namespace disconnect' || reason === 'server namespace disconnect' || reason === 'transport close') {
+        var username = connectedPlayers.get(socket.id);
+
+        if (username) {
+          var player = authorizedPlayers.get(username); // If player had created a game, remove it
+
+          removeAllGamesCreatedByPlayer(username).then(function (res) {
+            if (res.affectedRows > 0) {
+              // Tell other clients to reload their games if there were
+              forceClientsReloadGames();
+            }
+          }).catch(function (err) {
+            return console.log('Error removing game :>> ', err.message);
+          }); // Tell game manager a player left, if he was playing
+
+          if (player.isPlayingGameId >= 0) {
+            gamesManager.playerLeft(player, true);
+          }
+
+          connectedPlayers.delete(socket.id);
+          player.setConnected(false);
+          player.setSocket(null);
+          console.log(username + ' disconnected');
+        }
+      }
+    });
+    socket.on('connect', function () {
+      console.log('Connected to server');
+    });
+    socket.on('auth', /*#__PURE__*/function () {
+      var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(playerName, callback) {
+        var response, kickout;
+        return _regenerator.default.wrap(function _callee$(_context) {
+          while (1) {
+            switch (_context.prev = _context.next) {
+              case 0:
+                console.log(' Received auth message, player name : ' + playerName);
+                _context.next = 3;
+                return updatePlayersFromDb();
+
+              case 3:
+                response = {};
+                kickout = false;
+
+                if (authorizedPlayersNames.has(playerName)) {
+                  _context.next = 10;
+                  break;
+                }
+
+                response = {
+                  res: 'ko',
+                  message: playerName + ' Not found'
+                };
+                kickout = true;
+                _context.next = 31;
+                break;
+
+              case 10:
+                if (!(authorizedPlayers.get(playerName).isConnected() && authorizedPlayers.get(playerName).getSocket().connected)) {
+                  _context.next = 15;
+                  break;
+                }
+
+                response = {
+                  res: 'ko',
+                  message: playerName + ' already connected'
+                };
+                kickout = true;
+                _context.next = 31;
+                break;
+
+              case 15:
+                if (!authorizedPlayers.get(playerName).isConnected()) {
+                  _context.next = 19;
+                  break;
+                }
+
+                authorizedPlayers.get(playerName).getSocket().disconnect(true);
+                _context.next = 27;
+                break;
+
+              case 19:
+                _context.prev = 19;
+                _context.next = 22;
+                return removeAllGamesCreatedByPlayer(playerName);
+
+              case 22:
+                _context.next = 27;
+                break;
+
+              case 24:
+                _context.prev = 24;
+                _context.t0 = _context["catch"](19);
+                console.log('Error removing games : ' + _context.t0.message);
+
+              case 27:
+                response = {
+                  res: 'ok',
+                  message: playerName + ' connected'
+                };
+                authorizedPlayers.get(playerName).setConnected(true);
+                authorizedPlayers.get(playerName).setSocket(socket);
+                connectedPlayers.set(socket.id, playerName);
+
+              case 31:
+                callback(response);
+
+                if (kickout === true) {
+                  setTimeout(function () {
+                    socket.disconnect(true);
+                  }, 600);
+                }
+
+              case 33:
+              case "end":
+                return _context.stop();
+            }
+          }
+        }, _callee, null, [[19, 24]]);
+      }));
+
+      return function (_x, _x2) {
+        return _ref.apply(this, arguments);
+      };
+    }());
+    socket.on('games-list', /*#__PURE__*/function () {
+      var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(playerName, callback) {
+        var response, games;
+        return _regenerator.default.wrap(function _callee2$(_context2) {
+          while (1) {
+            switch (_context2.prev = _context2.next) {
+              case 0:
+                console.log(' Received games-list message, player name : ' + playerName);
+                response = {};
+                _context2.prev = 2;
+                _context2.next = 5;
+                return getJoinableGames(playerName);
+
+              case 5:
+                games = _context2.sent;
+                response = {
+                  res: 'ok',
+                  message: games
+                };
+                _context2.next = 12;
+                break;
+
+              case 9:
+                _context2.prev = 9;
+                _context2.t0 = _context2["catch"](2);
+                response = {
+                  res: 'ko',
+                  message: 'Error from server'
+                };
+
+              case 12:
+                callback(response);
+
+              case 13:
+              case "end":
+                return _context2.stop();
+            }
+          }
+        }, _callee2, null, [[2, 9]]);
+      }));
+
+      return function (_x3, _x4) {
+        return _ref2.apply(this, arguments);
+      };
+    }());
+    socket.on('create-game', /*#__PURE__*/function () {
+      var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(game, callback) {
+        var response, id;
+        return _regenerator.default.wrap(function _callee3$(_context3) {
+          while (1) {
+            switch (_context3.prev = _context3.next) {
+              case 0:
+                response = {};
+                _context3.prev = 1;
+                _context3.next = 4;
+                return addGameDb(game);
+
+              case 4:
+                id = _context3.sent;
+                response = {
+                  res: 'ok',
+                  message: id
+                };
+                gamesManager.addPlayerInGame(authorizedPlayers.get(connectedPlayers.get(socket.id)), id, false, true, {
+                  deck: game.deck,
+                  advRules: game.advRules
+                });
+                console.log('Force all clients to reload their games');
+                forceClientsReloadGames();
+                _context3.next = 15;
+                break;
+
+              case 11:
+                _context3.prev = 11;
+                _context3.t0 = _context3["catch"](1);
+                console.log('error in create-game:>> ', _context3.t0);
+                response = {
+                  res: 'ko',
+                  message: 'Error from server'
+                };
+
+              case 15:
+                callback(response);
+
+              case 16:
+              case "end":
+                return _context3.stop();
+            }
+          }
+        }, _callee3, null, [[1, 11]]);
+      }));
+
+      return function (_x5, _x6) {
+        return _ref3.apply(this, arguments);
+      };
+    }());
+    socket.on('join-game', /*#__PURE__*/function () {
+      var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(gameDetails, callback) {
+        var result, response;
+        return _regenerator.default.wrap(function _callee4$(_context4) {
+          while (1) {
+            switch (_context4.prev = _context4.next) {
+              case 0:
+                result = gamesManager.addPlayerInGame(authorizedPlayers.get(connectedPlayers.get(socket.id)), gameDetails.id, gameDetails.joinCreatedGame);
+                response = {};
+
+                if (result === true) {
+                  response.res = 'ok';
+                  response.message = 'Game joined';
+                } else {
+                  response.res = 'ko';
+                  response.message = 'Unable to join game';
+                }
+
+                callback(response);
+
+              case 4:
+              case "end":
+                return _context4.stop();
+            }
+          }
+        }, _callee4);
+      }));
+
+      return function (_x7, _x8) {
+        return _ref4.apply(this, arguments);
+      };
+    }());
+    socket.on('leave-game', /*#__PURE__*/function () {
+      var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(username, callback) {
+        var res;
+        return _regenerator.default.wrap(function _callee5$(_context5) {
+          while (1) {
+            switch (_context5.prev = _context5.next) {
+              case 0:
+                // Remove player from game he is playing
+                try {
+                  res = removeAllGamesCreatedByPlayer(username);
+
+                  if (res.affectedRows > 0) {
+                    forceClientsReloadGames();
+                  }
+                } catch (err) {
+                  console.log('Error removing games : ' + err.message);
+                }
+
+                gamesManager.playerLeft(authorizedPlayers.get(username), false);
+                callback(true);
+
+              case 3:
+              case "end":
+                return _context5.stop();
+            }
+          }
+        }, _callee5);
+      }));
+
+      return function (_x9, _x10) {
+        return _ref5.apply(this, arguments);
+      };
+    }());
+  });
+
+  var updatePlayersFromDb = /*#__PURE__*/function () {
+    var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {
+      var usernames;
+      return _regenerator.default.wrap(function _callee6$(_context6) {
+        while (1) {
+          switch (_context6.prev = _context6.next) {
+            case 0:
+              _context6.prev = 0;
+              _context6.next = 3;
+              return mariadbConn.getUsernames();
+
+            case 3:
+              usernames = _context6.sent;
+              authorizedPlayersNames = new Set(usernames);
+              console.log('authorizedPlayersNames update : ', authorizedPlayersNames);
+              authorizedPlayersNames.forEach(function (name) {
+                if (!authorizedPlayers.has(name)) {
+                  console.log('New username authorized :', name);
+                  authorizedPlayers.set(name, new _playerId.default(name));
+                }
+              });
+              return _context6.abrupt("return", 'ok');
+
+            case 10:
+              _context6.prev = 10;
+              _context6.t0 = _context6["catch"](0);
+              console.log('Error getting usernames from DB : ', _context6.t0);
+              return _context6.abrupt("return", _context6.t0.Error);
+
+            case 14:
+            case "end":
+              return _context6.stop();
+          }
+        }
+      }, _callee6, null, [[0, 10]]);
+    }));
+
+    return function updatePlayersFromDb() {
+      return _ref6.apply(this, arguments);
+    };
+  }();
+
+  var getCurrentListAuthorizedPlayers = function getCurrentListAuthorizedPlayers() {
+    return (0, _toConsumableArray2.default)(authorizedPlayersNames);
+  };
+
+  var addPlayerDb = /*#__PURE__*/function () {
+    var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(username) {
+      var response;
+      return _regenerator.default.wrap(function _callee7$(_context7) {
+        while (1) {
+          switch (_context7.prev = _context7.next) {
+            case 0:
+              console.log('add player in db : ' + username);
+              _context7.prev = 1;
+              _context7.next = 4;
+              return mariadbConn.addPlayer(username);
+
+            case 4:
+              response = _context7.sent;
+              authorizedPlayersNames.add(username);
+              authorizedPlayers.set(username, new _playerId.default(username));
+              return _context7.abrupt("return", 'OK ' + JSON.stringify(response));
+
+            case 10:
+              _context7.prev = 10;
+              _context7.t0 = _context7["catch"](1);
+              return _context7.abrupt("return", 'KO ' + _context7.t0.message);
+
+            case 13:
+            case "end":
+              return _context7.stop();
+          }
+        }
+      }, _callee7, null, [[1, 10]]);
+    }));
+
+    return function addPlayerDb(_x11) {
+      return _ref7.apply(this, arguments);
+    };
+  }();
+
+  var removePlayerDb = /*#__PURE__*/function () {
+    var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(username) {
+      var response;
+      return _regenerator.default.wrap(function _callee8$(_context8) {
+        while (1) {
+          switch (_context8.prev = _context8.next) {
+            case 0:
+              console.log('remove player in db : ' + username);
+              _context8.prev = 1;
+              _context8.next = 4;
+              return mariadbConn.removePlayer(username);
+
+            case 4:
+              response = _context8.sent;
+              authorizedPlayersNames.delete(username);
+              authorizedPlayers.delete(username);
+              return _context8.abrupt("return", 'OK ' + JSON.stringify(response));
+
+            case 10:
+              _context8.prev = 10;
+              _context8.t0 = _context8["catch"](1);
+              return _context8.abrupt("return", 'KO ' + _context8.t0.message);
+
+            case 13:
+            case "end":
+              return _context8.stop();
+          }
+        }
+      }, _callee8, null, [[1, 10]]);
+    }));
+
+    return function removePlayerDb(_x12) {
+      return _ref8.apply(this, arguments);
+    };
+  }();
+
+  var addGameDb = /*#__PURE__*/function () {
+    var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(game) {
+      var response;
+      return _regenerator.default.wrap(function _callee9$(_context9) {
+        while (1) {
+          switch (_context9.prev = _context9.next) {
+            case 0:
+              _context9.prev = 0;
+              _context9.next = 3;
+              return mariadbConn.addNewGame(game);
+
+            case 3:
+              response = _context9.sent;
+              return _context9.abrupt("return", response);
+
+            case 7:
+              _context9.prev = 7;
+              _context9.t0 = _context9["catch"](0);
+              console.log('KO ' + _context9.t0.message);
+              throw _context9.t0;
+
+            case 11:
+            case "end":
+              return _context9.stop();
+          }
+        }
+      }, _callee9, null, [[0, 7]]);
+    }));
+
+    return function addGameDb(_x13) {
+      return _ref9.apply(this, arguments);
+    };
+  }();
+
+  var removeGameDb = /*#__PURE__*/function () {
+    var _ref10 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10(method) {
+      var response,
+          _args10 = arguments;
+      return _regenerator.default.wrap(function _callee10$(_context10) {
+        while (1) {
+          switch (_context10.prev = _context10.next) {
+            case 0:
+              _context10.prev = 0;
+              _context10.t0 = method;
+              _context10.next = _context10.t0 === 'byId' ? 4 : _context10.t0 === 'byPlayer1' ? 8 : _context10.t0 === 'byPlayerAny' ? 12 : _context10.t0 === 'byStatus' ? 16 : _context10.t0 === 'byDays' ? 20 : _context10.t0 === 'finishedByDays' ? 24 : 28;
+              break;
+
+            case 4:
+              _context10.next = 6;
+              return mariadbConn.removeGameById(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 6:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 8:
+              _context10.next = 10;
+              return mariadbConn.removeGamesByPlayer1(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 10:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 12:
+              _context10.next = 14;
+              return mariadbConn.removeGamesByPlayerAny(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 14:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 16:
+              _context10.next = 18;
+              return mariadbConn.removeGamesByStatus(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 18:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 20:
+              _context10.next = 22;
+              return mariadbConn.removeGamesByDays(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 22:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 24:
+              _context10.next = 26;
+              return mariadbConn.removeFinishedGamesByDays(_args10.length <= 1 ? undefined : _args10[1]);
+
+            case 26:
+              response = _context10.sent;
+              return _context10.abrupt("break", 29);
+
+            case 28:
+              return _context10.abrupt("return", 'KO : Unknown method to remove game from DB : ' + method);
+
+            case 29:
+              return _context10.abrupt("return", 'OK ' + JSON.stringify(response));
+
+            case 32:
+              _context10.prev = 32;
+              _context10.t1 = _context10["catch"](0);
+              return _context10.abrupt("return", 'KO ' + _context10.t1.message);
+
+            case 35:
+            case "end":
+              return _context10.stop();
+          }
+        }
+      }, _callee10, null, [[0, 32]]);
+    }));
+
+    return function removeGameDb(_x14) {
+      return _ref10.apply(this, arguments);
+    };
+  }();
+
+  var getJoinableGames = /*#__PURE__*/function () {
+    var _ref11 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(username) {
+      var games;
+      return _regenerator.default.wrap(function _callee11$(_context11) {
+        while (1) {
+          switch (_context11.prev = _context11.next) {
+            case 0:
+              _context11.prev = 0;
+              _context11.next = 3;
+              return mariadbConn.getJoinableGamesForPlayer(username);
+
+            case 3:
+              games = _context11.sent;
+              return _context11.abrupt("return", games);
+
+            case 7:
+              _context11.prev = 7;
+              _context11.t0 = _context11["catch"](0);
+              console.log('KO : not able to get games : ' + _context11.t0);
+              throw _context11.t0;
+
+            case 11:
+            case "end":
+              return _context11.stop();
+          }
+        }
+      }, _callee11, null, [[0, 7]]);
+    }));
+
+    return function getJoinableGames(_x15) {
+      return _ref11.apply(this, arguments);
+    };
+  }();
+
+  var removeAllGamesCreatedByPlayer = /*#__PURE__*/function () {
+    var _ref12 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(playerName) {
+      var result;
+      return _regenerator.default.wrap(function _callee12$(_context12) {
+        while (1) {
+          switch (_context12.prev = _context12.next) {
+            case 0:
+              _context12.next = 2;
+              return mariadbConn.removeCreatedGamesByPlayer1(playerName);
+
+            case 2:
+              result = _context12.sent;
+              return _context12.abrupt("return", result);
+
+            case 4:
+            case "end":
+              return _context12.stop();
+          }
+        }
+      }, _callee12);
+    }));
+
+    return function removeAllGamesCreatedByPlayer(_x16) {
+      return _ref12.apply(this, arguments);
+    };
+  }();
+
+  var forceClientsReloadGames = function forceClientsReloadGames() {
+    server.emit('reload-games-list');
+  };
+
+  return {
+    updatePlayersFromDb: updatePlayersFromDb,
+    getCurrentListAuthorizedPlayers: getCurrentListAuthorizedPlayers,
+    addPlayerDb: addPlayerDb,
+    removePlayerDb: removePlayerDb,
+    addGameDb: addGameDb,
+    removeGameDb: removeGameDb,
+    getJoinableGames: getJoinableGames
+  };
+}
+
+var server = new Server();
+var serverToolListner = new _serverToolListener.default(server);
+serverToolListner.listen(1664);
+//# sourceMappingURL=server.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/server.js.map


+ 193 - 0
server/dist/tools/server-tool-listener.js

@@ -0,0 +1,193 @@
+'use strict';
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
+
+var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
+
+var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
+
+var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
+
+var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
+
+var _socket = _interopRequireDefault(require("socket.io"));
+
+var ServerToolListner = /*#__PURE__*/function () {
+  function ServerToolListner(server) {
+    (0, _classCallCheck2.default)(this, ServerToolListner);
+    this.server = server;
+  }
+
+  (0, _createClass2.default)(ServerToolListner, [{
+    key: "listen",
+    value: function listen(port) {
+      var server = this.server;
+      this.serverTool = _socket.default.listen(port);
+      this.serverTool.on('connection', function (socket) {
+        console.log('tool connected');
+        socket.on('players', function (option, callback) {
+          callback(server.getCurrentListAuthorizedPlayers());
+          socket.disconnect(false);
+        });
+        socket.on('syncdb', /*#__PURE__*/function () {
+          var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(option, callback) {
+            var response;
+            return _regenerator.default.wrap(function _callee$(_context) {
+              while (1) {
+                switch (_context.prev = _context.next) {
+                  case 0:
+                    _context.next = 2;
+                    return server.updatePlayersFromDb();
+
+                  case 2:
+                    response = _context.sent;
+                    callback(response);
+                    socket.disconnect(false);
+
+                  case 5:
+                  case "end":
+                    return _context.stop();
+                }
+              }
+            }, _callee);
+          }));
+
+          return function (_x, _x2) {
+            return _ref.apply(this, arguments);
+          };
+        }());
+        socket.on('add-player', /*#__PURE__*/function () {
+          var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(username, callback) {
+            var response;
+            return _regenerator.default.wrap(function _callee2$(_context2) {
+              while (1) {
+                switch (_context2.prev = _context2.next) {
+                  case 0:
+                    _context2.next = 2;
+                    return server.addPlayerDb.apply(server, (0, _toConsumableArray2.default)(username));
+
+                  case 2:
+                    response = _context2.sent;
+                    callback(response);
+                    socket.disconnect(false);
+
+                  case 5:
+                  case "end":
+                    return _context2.stop();
+                }
+              }
+            }, _callee2);
+          }));
+
+          return function (_x3, _x4) {
+            return _ref2.apply(this, arguments);
+          };
+        }());
+        socket.on('remove-player', /*#__PURE__*/function () {
+          var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(username, callback) {
+            var response;
+            return _regenerator.default.wrap(function _callee3$(_context3) {
+              while (1) {
+                switch (_context3.prev = _context3.next) {
+                  case 0:
+                    _context3.next = 2;
+                    return server.removePlayerDb.apply(server, (0, _toConsumableArray2.default)(username));
+
+                  case 2:
+                    response = _context3.sent;
+                    callback(response);
+                    socket.disconnect(false);
+
+                  case 5:
+                  case "end":
+                    return _context3.stop();
+                }
+              }
+            }, _callee3);
+          }));
+
+          return function (_x5, _x6) {
+            return _ref3.apply(this, arguments);
+          };
+        }());
+        socket.on('add-game', /*#__PURE__*/function () {
+          var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(game, callback) {
+            var response;
+            return _regenerator.default.wrap(function _callee4$(_context4) {
+              while (1) {
+                switch (_context4.prev = _context4.next) {
+                  case 0:
+                    response = '';
+                    _context4.prev = 1;
+                    _context4.next = 4;
+                    return server.addGameDb.apply(server, (0, _toConsumableArray2.default)(game));
+
+                  case 4:
+                    response = _context4.sent;
+                    response = 'OK - inserted at id :' + response;
+                    _context4.next = 11;
+                    break;
+
+                  case 8:
+                    _context4.prev = 8;
+                    _context4.t0 = _context4["catch"](1);
+                    response = 'KO ' + _context4.t0.message;
+
+                  case 11:
+                    callback(JSON.stringify(response));
+                    socket.disconnect(false);
+
+                  case 13:
+                  case "end":
+                    return _context4.stop();
+                }
+              }
+            }, _callee4, null, [[1, 8]]);
+          }));
+
+          return function (_x7, _x8) {
+            return _ref4.apply(this, arguments);
+          };
+        }());
+        socket.on('remove-game', /*#__PURE__*/function () {
+          var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(options, callback) {
+            var response;
+            return _regenerator.default.wrap(function _callee5$(_context5) {
+              while (1) {
+                switch (_context5.prev = _context5.next) {
+                  case 0:
+                    _context5.next = 2;
+                    return server.removeGameDb.apply(server, (0, _toConsumableArray2.default)(options));
+
+                  case 2:
+                    response = _context5.sent;
+                    callback(response);
+                    socket.disconnect(false);
+
+                  case 5:
+                  case "end":
+                    return _context5.stop();
+                }
+              }
+            }, _callee5);
+          }));
+
+          return function (_x9, _x10) {
+            return _ref5.apply(this, arguments);
+          };
+        }());
+      });
+    }
+  }]);
+  return ServerToolListner;
+}();
+
+exports.default = ServerToolListner;
+//# sourceMappingURL=server-tool-listener.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/tools/server-tool-listener.js.map


+ 68 - 0
server/dist/tools/server-tools.js

@@ -0,0 +1,68 @@
+'use strict'; // process.exit(1);
+
+var usage = function usage() {
+  var text = "\n    USAGE : npm run tools <option>\n      with <option> :\n       - players : get authorized players\n       - add-player <username> : Add a new authorized player\n       - remove-player <username> : Remove an authorized player\n       - syncdb : Force server to sync with db\n       - add-game <json>: Add new game into DB\n          '{\"player1\":\"\",\"player2\":\"\",\"deck\":\"\",\"advRules\":[],\"status\":\"\",\"data\":\"{}\"}'\n            deck     : faction tournament draft  (one of these)\n            advRules : popularity discard        (0 or any of these)\n            status   : CREATED ONGOING FINISHED  (one of these)\n       - remove-game <method> <filter> : Remove game(s) from DB\n          <method>          /  <filter>\n            byId                 gameId\n            byPlayer1            username\n            byPlayerAny          username\n            byStatus             CREATED ONGOING FINISHED  (one of these)\n            byDays               number of days old is last connection\n            finishedByDays       number of days old is last connection(only finished)\n       ";
+  console.log(text);
+};
+
+var args = process.argv.slice(2);
+
+if (args.length === 0) {
+  usage();
+  process.exit(0);
+}
+
+var command = '';
+var option = '';
+
+switch (args[0]) {
+  case 'players':
+  case 'syncdb':
+    command = args[0];
+    break;
+
+  case 'add-player':
+  case 'remove-player':
+  case 'add-game':
+  case 'remove-game':
+    if (args[0] === 'remove-game' && args.length < 3) {
+      usage();
+      process.exit(0);
+    }
+
+    if (args[1] && args[1] !== '') {
+      command = args[0];
+
+      if (args[0] === 'add-game') {
+        try {
+          option = [JSON.parse(args[1])];
+        } catch (error) {
+          console.log(error);
+          process.exit(0);
+        }
+      } else {
+        args.shift();
+        option = args;
+      }
+    } else {
+      console.log('error ; specify a username');
+      usage();
+    }
+
+    break;
+
+  default:
+    usage();
+    break;
+}
+
+if (command !== '') {
+  console.log('Connecting to server...');
+
+  var socket = require('socket.io-client')('http://localhost:1664');
+
+  socket.emit(command, option, function (response) {
+    console.log('Done, response from server : ', response);
+  });
+}
+//# sourceMappingURL=server-tools.js.map

File diff suppressed because it is too large
+ 0 - 0
server/dist/tools/server-tools.js.map


+ 0 - 97
server/game-server/online-duel-sync.js

@@ -1,97 +0,0 @@
-const fs = require('fs');
-
-export default function OnlineDuelSync(
-  ioServer,
-  mariadbConnector,
-  gameId,
-  firstPlayer
-) {
-  let io = ioServer;
-  let mdb = mariadbConnector;
-  let players = new Map();
-  players.set(firstPlayer.playerName, firstPlayer);
-
-  let loadGameData = function() {
-    let gameDetails = {};
-    try {
-      gameDetails = mdb.getGameById(gameId);
-    } catch (err) {
-      console.log('Error retreiving game : ', err.message);
-    }
-    return gameDetails;
-  };
-
-  let chat = function(name, message) {
-    io.to(gameId).emit('message', {
-      from: name,
-      text: message
-    });
-  };
-
-  firstPlayer
-    .getSocket()
-    .join(gameId, () => {
-      chat(
-        firstPlayer.playerName,
-        firstPlayer.playerName +
-          ' has entered the game, waiting for second player...'
-      );
-    })
-    .emit('update-game-data', loadGameData().game_data)
-    .on('chat', message => {
-      chat(firstPlayer.playerName, message);
-    });
-
-  let addPlayer = function(player) {
-    let res = true;
-    if (players.size === 1) {
-      if (players.has(player.playerName)) {
-        console.log(
-          'ERROR :  this player is already there ! ',
-          player.playerName
-        );
-        return false;
-      }
-      players.set(player.playerName, player);
-      player
-        .getSocket()
-        .join(gameId, () => {
-          chat(player.playerName, player.playerName + ' has entered the game');
-        })
-        .emit('update-game-data', loadGameData().game_data)
-        .on('chat', message => {
-          chat(player.playerName, message);
-        });
-    } else {
-      console.log(
-        'Already 2 players, weird :' +
-          player.playerName +
-          ' was trying to join : ',
-        players
-      );
-      res = false;
-    }
-    return res;
-  };
-
-  let playerLeft = function(player, disconnected) {
-    let playerLeaving = players.get(player.playerName);
-    if (playerLeaving) {
-      if (!disconnected) {
-        playerLeaving.getSocket().leave(gameId);
-      }
-
-      chat(player.playerName, player.playerName + ' has left the game');
-      players.delete(player.playerName);
-    }
-  };
-
-  let hasPlayers = function() {
-    return players.size > 0;
-  };
-  return {
-    addPlayer,
-    playerLeft,
-    hasPlayers
-  };
-}

File diff suppressed because it is too large
+ 868 - 17
server/package-lock.json


+ 6 - 2
server/package.json

@@ -7,8 +7,8 @@
   ],
   "scripts": {
     "start": "babel-node server.js",
-    "server-dev": "babel-node -r ../node_modules/dotenv/config server.js dotenv_config_path=../.env.development",
-    "tools": "node tools/server-tools.js"
+    "server-dev": "babel-node -r ../node_modules/dotenv/config src/server.js dotenv_config_path=../.env.development",
+    "tools": "node src/tools/server-tools.js"
   },
   "author": "JOJO",
   "license": "MIT",
@@ -17,9 +17,13 @@
     "socket.io": "^2.3.0"
   },
   "devDependencies": {
+    "@babel/cli": "^7.8.4",
+    "@babel/core": "^7.9.6",
     "@babel/node": "7.6.2",
     "@babel/plugin-proposal-object-rest-spread": "^7.9.0",
+    "@babel/plugin-transform-runtime": "^7.9.6",
     "@babel/preset-env": "7.6.2",
+    "@babel/register": "^7.9.0",
     "socket.io-client": "^2.3.0"
   }
 }

+ 1 - 1
src/server-shared/all-heroes.json → server/src/client-server-shared/all-heroes.json

@@ -1,5 +1,5 @@
 {
-    "version": "1.0.0",
+    "version": "2.0.0",
     "heroes": [
       {
         "name" : "Paysan",

+ 149 - 0
server/src/client-server-shared/duel-controller.js

@@ -0,0 +1,149 @@
+'use strict';
+
+/* This file will be used by server for online game, and by app for local game
+Therfore it should not have any dependency on any of those environments. 
+
+It is a state machine that follows the game steps
+
+It receives a dataStore to get and share data but doesn't care what is 
+behind (socket.io comm for online, direct call to vuex store for local)
+Is gives an interface in order to receive notifications
+
+Receiving interface : 
+  - dataStore : provides methods :
+    - .load()          : to get saved game data
+    - .save(data)      : to store game data and propagate (returns true if it succeeded, false otherwise)
+    - .getHeroesJson() : to get JSON file with all heroes cards described 
+   
+Provides interface :
+  - duelControllerProxy : provides methods :
+    - endTurn(data) : notifies the controller that players ended current turn with attached data */
+
+import { initHeroesFromJson } from './heroesHelper';
+import gameStates from './gameStates/gameStates';
+const DuelController = function(dataStore) {
+  let gameData = {
+    game: {},
+    bluePlayer: {},
+    redPlayer: {}
+  };
+  let allHeroes = null;
+
+  /** ***** STATE MACHINE internal methods ***** */
+
+  // Holds current running state
+  let currentGameState = null;
+
+  // Set new current State
+  let setCurrentState = stateName => {
+    currentGameState = states[stateName];
+    gameData.game.gameState = stateName;
+  };
+
+  // get new State
+  let getCurrentState = () => {
+    return currentGameState;
+  };
+
+  // Start the current state
+  let startCurrentState = (payload = null) => {
+    currentGameState.start(payload);
+  };
+
+  // Update the current state
+  let updateCurrentState = (payload = null) => {
+    currentGameState.update(payload);
+  };
+
+  // End the current state
+  let endCurrentState = (payload = null) => {
+    currentGameState.end(payload);
+  };
+
+  let getGameData = () => gameData;
+
+  // Interface to set data and store it
+  let storeData = data => {
+    setData(data);
+    return dataStore.save(gameData);
+  };
+
+  // Interface to set data only
+  let setData = data => {
+    gameData = data;
+  };
+
+  // Interface to load data
+  let loadData = () => {
+    gameData = dataStore.load();
+  };
+
+  // Interface to get set of all heroes
+  let getAllHeroes = () => {
+    if (!allHeroes) {
+      allHeroes = initHeroesFromJson(dataStore.getHeroesJson());
+    }
+    return allHeroes;
+  };
+
+  // Pass those state machine methods to each state
+  let stateCtrl = {
+    setCurrentState,
+    getCurrentState,
+    startCurrentState,
+    updateCurrentState,
+    endCurrentState,
+    storeData,
+    setData,
+    getGameData,
+    loadData,
+    getAllHeroes
+  };
+
+  // declare all states
+  // First state to be set after public APIs (start or resume game) are called
+  let states = {
+    '0_INIT': new gameStates.initState(stateCtrl),
+    '1_SELECT_FACTION': new gameStates.selectFactionState(stateCtrl)
+  };
+
+  /** ***** Following are public methods ***** */
+
+  // Method to create and start a new game (both players are there)
+  let startNewGame = function(player1Name, player2Name, gameOptions) {
+    // 1st state is init
+    setCurrentState('0_INIT');
+    //Start it
+    startCurrentState({
+      player1Name,
+      player2Name,
+      gameOptions
+    });
+  };
+
+  // Method to resume existing game, gameData is passed already
+  let resumeGame = function(data) {
+    gameData = data;
+    // Just set state per data in DB
+    // This state will wait for update
+    setCurrentState(gameData.gameState);
+  };
+
+  // Provide an interface so that players can notify the DuelController
+  let DuelControllerProxy = () => {
+    return {
+      endTurn: data => {
+        currentGameState.update(data);
+      }
+    };
+  };
+
+  // Return public methods
+  return {
+    startNewGame,
+    resumeGame,
+    duelControllerProxy: new DuelControllerProxy()
+  };
+};
+
+export default DuelController;

+ 50 - 0
server/src/client-server-shared/gameStates/0-initState.js

@@ -0,0 +1,50 @@
+'use strict';
+export default function(duelController) {
+  let duelCtrl = duelController;
+  let start = (payload = null) => {
+    if (payload) {
+      duelCtrl.setData({
+        bluePlayer: {
+          name: payload.player1Name,
+          color: 'blue'
+        },
+        redPlayer: {
+          name: payload.player2Name,
+          color: 'red'
+        },
+        game: {
+          deckMode: payload.gameOptions.deck,
+          advRules: payload.gameOptions.advRules,
+          waitingForBlue: false,
+          waitingForRed: false,
+          currentPlayer: '',
+          'battleTiles/left': [{ id: 0, name: 'mine', redPoints: 3 }]
+        }
+      });
+      // Init state just initialize game data, end it
+      duelCtrl.endCurrentState();
+    }
+  };
+  let update = () => {};
+  let end = () => {
+    switch (duelCtrl.getGameData().game.deckMode) {
+      case 'faction':
+        duelCtrl.setCurrentState('1_SELECT_FACTION');
+        duelCtrl.startCurrentState();
+        break;
+      case 'tournament':
+        break;
+
+      case 'draft':
+        break;
+
+      default:
+        break;
+    }
+  };
+  return {
+    start,
+    update,
+    end
+  };
+}

+ 40 - 0
server/src/client-server-shared/gameStates/1-selectFactionState.js

@@ -0,0 +1,40 @@
+'use strict';
+import { getHeroesByFaction } from '../heroesHelper';
+export default function selectFactionState(duelController) {
+  let duelCtrl = duelController;
+  let start = () => {
+    let gameData = duelCtrl.getGameData();
+    gameData.game.heroes = duelCtrl.getAllHeroes();
+    gameData.game['waitingFor/blue'] = true;
+    gameData.game['waitingFor/red'] = true;
+    gameData.game.currentPlayer = 'both';
+    duelCtrl.storeData(gameData);
+  };
+
+  let update = (payload = null) => {
+    let gameData = duelCtrl.getGameData();
+    if (payload) {
+      let player = payload.player + 'player';
+      gameData[player].faction = payload.faction;
+      gameData[player].twelveHeroes = getHeroesByFaction(payload.faction);
+      gameData.game['waitingFor/' + payload.player] = false;
+      duelCtrl.storeData(gameData);
+    }
+
+    if (
+      duelCtrl.gameData.game['waitingFor/blue'] === false &&
+      duelCtrl.gameData.game['waitingFor/red'] === false
+    ) {
+      duelCtrl.endCurrentState();
+    }
+  };
+
+  let end = () => {
+    console.log('Go to next game state : 2_CHANGE_UP_TO_3');
+  };
+  return {
+    start,
+    update,
+    end
+  };
+}

+ 16 - 0
server/src/client-server-shared/gameStates/gameStates.js

@@ -0,0 +1,16 @@
+'use strict';
+// Following contains all states definitions
+// They all receive interface from duel controller as an argument (see stateCtrl in DuelController)
+// They all must implement :
+//  - start  : where it prepares the game data for this step and save it dor players
+//  - update : where it waits for players inputs, save them
+//  - end    : where it decides what should be next state
+// Those 3 methods might receive a payload but not necessarily
+// The state have also direct acces to game data
+
+import initState from './0-initState';
+import selectFactionState from './1-selectFactionState';
+export default {
+  initState,
+  selectFactionState
+};

+ 64 - 0
server/src/client-server-shared/heroesHelper.js

@@ -0,0 +1,64 @@
+'use strict';
+function shuffle(a) {
+  for (let i = a.length - 1; i > 0; i--) {
+    const j = Math.floor(Math.random() * (i + 1));
+    [a[i], a[j]] = [a[j], a[i]];
+  }
+  return a;
+}
+
+export const initHeroesFromJson = function(allHeroesJson) {
+  let abilitiesMap = new Map();
+  allHeroesJson.abilities.forEach(ability => {
+    abilitiesMap.set(ability.abilityName, {
+      name: ability.abilityName,
+      hook: ability.abilityHook,
+      isOptionnal: ability.optionnal,
+      desc: ability['abilityDesc-FR']
+    });
+  });
+  let heroes = [];
+  let heroUniqueId = 0;
+  allHeroesJson.heroes.forEach(hero => {
+    let i = 0;
+    while (i < hero.nbInDeck) {
+      heroes.push({
+        id: heroUniqueId,
+        name: hero.name,
+        cost: hero.cost,
+        power: hero.power,
+        faction: hero.faction,
+        ability: abilitiesMap.get(hero.ability),
+        isDraftable: hero.draftMode,
+        popularity: hero.popularity
+      });
+      heroUniqueId++;
+      i++;
+    }
+  });
+  return heroes;
+};
+export const getHeroesByFaction = function(allHeroes, faction) {
+  return allHeroes.filter(hero => {
+    return hero.faction === faction;
+  });
+};
+export const getDraftSets = function(allHeroes) {
+  let heroesDraftable = allHeroes.filter(hero => {
+    return hero.isDraftable === true;
+  });
+
+  let shuffledHeroes = shuffle(heroesDraftable);
+
+  // Return 4 sets of 6 heroes for draft mode
+  let draftSets = [
+    shuffledHeroes.slice(0, 5),
+    shuffledHeroes.slice(6, 11),
+    shuffledHeroes.slice(12, 17),
+    shuffledHeroes.slice(18, 23)
+  ];
+  return draftSets;
+};
+export const shuffleHeroes = function(heroes) {
+  return shuffle(heroes);
+};

+ 41 - 10
server/db/mariadb-connector.js → server/src/db/mariadb-connector.js

@@ -51,7 +51,6 @@ export default class MariadbConnector {
   }
 
   async addPlayer(username) {
-    console.log('mariadb add player : ', username);
     let conn;
     try {
       conn = await this.pool.getConnection();
@@ -59,7 +58,6 @@ export default class MariadbConnector {
         'INSERT INTO players value (?, NOW(),NULL)',
         [username]
       );
-      console.log('OK removing player : ', val);
 
       return val;
     } catch (err) {
@@ -72,14 +70,12 @@ export default class MariadbConnector {
     }
   }
   async removePlayer(username) {
-    console.log('mariadb remove player : ', username);
     let conn;
     try {
       conn = await this.pool.getConnection();
       const val = await conn.query('DELETE FROM players WHERE username=?', [
         username
       ]);
-      console.log('OK removing player : ', val);
       return val;
     } catch (err) {
       console.log('error removing player : ', err);
@@ -92,7 +88,6 @@ export default class MariadbConnector {
   }
 
   async addNewGame(game) {
-    console.log('mariadb add game : ', game);
     let conn;
     try {
       conn = await this.pool.getConnection();
@@ -108,7 +103,6 @@ export default class MariadbConnector {
         ]
       );
 
-      console.log('OK adding game : ', val);
       return val.insertId;
     } catch (err) {
       console.log('error adding game : ', err);
@@ -155,12 +149,10 @@ export default class MariadbConnector {
   }
 
   async removeGame(queryStr, queryArgs) {
-    console.log('mariadb remove game  : ', queryStr);
     let conn;
     try {
       conn = await this.pool.getConnection();
       const val = await conn.query(queryStr, queryArgs);
-      console.log('OK removing game : ', val);
       return val;
     } catch (err) {
       console.log('error removing game : ', err);
@@ -178,13 +170,12 @@ export default class MariadbConnector {
     try {
       conn = await this.pool.getConnection();
       const res = await conn.query(
-        "SELECT * FROM games WHERE (status = 'CREATED' OR status = 'PAUSED')  AND(player1 = ? OR player2 = '' OR player2 = ?)",
+        "SELECT * FROM games WHERE (status = 'CREATED' OR status = 'PAUSED' OR status = 'PLAYING')  AND(player1 = ? OR player2 = '' OR player2 = ?)",
         [username, username]
       );
       for (const { id, player1, player2, deck, adv_rules } of res) {
         games.push({ id, player1, player2, deck, adv_rules });
       }
-      console.log(`returning for ${username} : `, games);
     } finally {
       if (conn && conn !== null) {
         conn.end();
@@ -205,6 +196,46 @@ export default class MariadbConnector {
     }
     return game;
   }
+  async updateGameStatus(id, status, player2 = null) {
+    let conn;
+    let result = {};
+    try {
+      conn = await this.pool.getConnection();
+      if (player2) {
+        result = await conn.query(
+          'UPDATE games SET player2=?, status = ?, last_played = NOW() WHERE games.id =?',
+          [player2, status, id]
+        );
+      } else {
+        result = await conn.query(
+          'UPDATE games SET status = ?, last_played = NOW() WHERE games.id =?',
+          [status, id]
+        );
+      }
+    } finally {
+      if (conn && conn !== null) {
+        conn.end();
+      }
+    }
+    return result;
+  }
+  async updateGameData(id, data) {
+    let conn;
+    let result = {};
+    let dataJson = JSON.stringify(data);
+    try {
+      conn = await this.pool.getConnection();
+      result = await conn.query(
+        'UPDATE games SET game_data = ?, last_played = NOW() WHERE games.id =?',
+        [dataJson, id]
+      );
+    } finally {
+      if (conn && conn !== null) {
+        conn.end();
+      }
+    }
+    return result;
+  }
 }
 let convertAdvRulesToString = function(advRulesArray) {
   let advRulestStr = '';

+ 32 - 4
server/game-server/games-manager.js → server/src/game-server/games-manager.js

@@ -1,18 +1,45 @@
+'use strict';
 import OnlineDuelSync from './online-duel-sync';
-
+const fs = require('fs');
+const path = require('path');
 export default function GamesManager(ioServer, mariadbConn) {
   let currentGames = new Map();
 
+  // We create Heroes data from JSON file, it will be needed by the duelController
+  // DuelController has no dependency to browser env nor node env. So it cannot read the
+  // file itself (not same mechanism, in node we use fs readFileSync)
+  // Read once here for all the games to come
+  let allHeroesJson = JSON.parse(
+    fs.readFileSync(
+      path.resolve(__dirname, '../client-server-shared/all-heroes.json')
+    )
+  );
+
   // Method to add player into a game, create or add to game syncer
-  let addPlayerInGame = function(player, gameId, joinCreatedGame) {
+  let addPlayerInGame = function(
+    player,
+    gameId,
+    joinCreatedGame,
+    isNewGame = false,
+    gameOptions = {}
+  ) {
     if (currentGames.has(gameId)) {
       player.isPlayingGameId = gameId;
-      return currentGames.get(gameId).addPlayer(player);
+      let duelSync = currentGames.get(gameId);
+      return duelSync.addPlayer(player);
     } else if (joinCreatedGame === false) {
       player.isPlayingGameId = gameId;
       currentGames.set(
         gameId,
-        new OnlineDuelSync(ioServer, mariadbConn, gameId, player)
+        new OnlineDuelSync(
+          ioServer,
+          mariadbConn,
+          gameId,
+          player,
+          isNewGame,
+          gameOptions,
+          allHeroesJson
+        )
       );
       return true;
 
@@ -34,6 +61,7 @@ export default function GamesManager(ioServer, mariadbConn) {
       currentGames.get(id).playerLeft(player, disconnected);
       if (!currentGames.get(id).hasPlayers()) {
         currentGames.delete(id);
+        mariadbConn.updateGameStatus(id, 'PAUSED');
       }
     }
 

+ 187 - 0
server/src/game-server/online-duel-sync.js

@@ -0,0 +1,187 @@
+'use strict';
+import DuelController from '../client-server-shared/duel-controller';
+
+export default function OnlineDuelSync(
+  ioServer,
+  mariadbConnector,
+  gameId,
+  firstPlayer,
+  isNewGame,
+  gameOptions,
+  allHeroesJson
+) {
+  let io = ioServer;
+  let mdb = mariadbConnector;
+  let isNew = isNewGame;
+  let heroesJson = allHeroesJson;
+  let players = new Map();
+
+  // internal method to load game data from DB
+  let loadGameData = async function() {
+    let gameDetails = { game_data: {} };
+    try {
+      let game = await mdb.getGameById(gameId);
+      gameDetails = game[0];
+      try {
+        gameDetails.game_data = JSON.parse(game[0].game_data);
+      } catch (err) {
+        console.log('err converting JSON data :>> ', err);
+        gameDetails.game_data = {};
+      }
+    } catch (err) {
+      console.log('Error retreiving game : ', err.message);
+    }
+    return gameDetails;
+  };
+
+  // Internal method to send message to game room
+  let chat = function(name, message) {
+    io.to(gameId).emit('message', {
+      from: name,
+      text: message
+    });
+  };
+
+  //dataStore will be used by the duel controller to save data and notify
+  let dataStore = {
+    load() {},
+    // Save new game data after statemachine processed it
+    save(data) {
+      try {
+        mdb.updateGameData(gameId, data);
+        io.to(gameId).emit('update-game-data', data);
+        return true;
+      } catch (err) {
+        return false;
+      }
+    },
+    getHeroesJson() {
+      return heroesJson;
+    }
+  };
+
+  // Create Duel Controller (game state machine)
+  // Give it possibility to store data and notify players with dataStore
+  let duelController = new DuelController(dataStore);
+
+  // Is used to notify duel controller of a player action
+  // gives access to method : .endTurn(data)
+  let duelCtrlProxy = duelController.duelControllerProxy;
+
+  // Set array of players with first one that joined
+  players.set(firstPlayer.playerName, firstPlayer);
+
+  let game_data = {};
+  loadGameData().then(gameDetails => {
+    game_data = gameDetails.game_data;
+    // Join 1st player to game room and notify it
+    // Also send the game data (might be empty for new game but it is ok)
+    firstPlayer
+      .getSocket()
+      .join(gameId, () => {
+        chat(
+          firstPlayer.playerName,
+          firstPlayer.playerName +
+            ' has entered the game, waiting for second player...'
+        );
+      })
+      .emit('update-game-data', game_data)
+      // This allows the player to send message to the game room
+      .on('chat', message => {
+        chat(firstPlayer.playerName, message);
+      })
+      .on('end-turn', data => {
+        duelCtrlProxy.endTurn(data);
+      });
+
+    // If not new game, set status to PLAYING right now
+    // Otherwise we wait second player before doing so
+    if (!isNew) {
+      mdb.updateGameStatus(gameId, 'PLAYING');
+      // Resume game right away, we don't need second player yet as it might be 1st player turn
+      duelController.resumeGame(game_data);
+    }
+  });
+
+  // Public method to add a second player to the game
+  let addPlayer = function(player) {
+    let res = true;
+    // Doing some checks
+    if (players.size === 1) {
+      if (players.has(player.playerName)) {
+        console.log(
+          'ERROR :  this player is already there ! ',
+          player.playerName
+        );
+        return false;
+      }
+      players.set(player.playerName, player);
+      loadGameData().then(gameDetails => {
+        game_data = gameDetails.game_data;
+        // second player is in ! notify the game room and send latest game data (ok if empty)
+        player
+          .getSocket()
+          .join(gameId, () => {
+            chat(
+              player.playerName,
+              player.playerName + ' has entered the game'
+            );
+          })
+          .emit('update-game-data', game_data)
+          // Allow 2nd player to chat in the room
+          .on('chat', message => {
+            chat(player.playerName, message);
+          })
+          .on('end-turn', data => {
+            duelCtrlProxy.endTurn(data);
+          });
+
+        // Both players connected to a new game, set status to PLAYING
+        if (isNew) {
+          isNew = false;
+          mdb.updateGameStatus(gameId, 'PLAYING', player.playerName);
+
+          // Start the duel controller
+          duelController.startNewGame(
+            firstPlayer.playerName,
+            player.playerName,
+            gameOptions
+          );
+        }
+      });
+    } else {
+      console.log(
+        'Already 2 players, weird :' +
+          player.playerName +
+          ' was trying to join : ',
+        players
+      );
+      res = false;
+    }
+    return res;
+  };
+
+  // Handle player disconnection / reconnection without impacting other player
+  // If no player left, this object will be destroyed by games-manager
+  let playerLeft = function(player, disconnected) {
+    let playerLeaving = players.get(player.playerName);
+    if (playerLeaving) {
+      if (!disconnected) {
+        playerLeaving.getSocket().leave(gameId);
+      }
+
+      chat(player.playerName, player.playerName + ' has left the game');
+      players.delete(player.playerName);
+    }
+  };
+
+  // Public methods to know if there are currently any player tie to this game
+  let hasPlayers = function() {
+    return players.size > 0;
+  };
+  return {
+    addPlayer,
+    playerLeft,
+    hasPlayers
+  };
+}

+ 0 - 0
server/players/player-id.js → server/src/players/player-id.js


+ 13 - 18
server/server.js → server/src/server.js

@@ -18,8 +18,6 @@ function Server() {
   let authorizedPlayersNames = new Set();
 
   server.on('connection', function(socket) {
-    console.log('A player connected with id : ' + socket.id);
-
     socket.on('disconnect', reason => {
       console.log('A player disconnected, reason : ' + reason);
       if (
@@ -31,10 +29,6 @@ function Server() {
         if (username) {
           let player = authorizedPlayers.get(username);
 
-          // First, tell game manager a player left, is he was playing
-          if (player.isPlayingGameId >= 0) {
-            gamesManager.playerLeft(player, true);
-          }
           // If player had created a game, remove it
           removeAllGamesCreatedByPlayer(username)
             .then(res => {
@@ -44,6 +38,10 @@ function Server() {
               }
             })
             .catch(err => console.log('Error removing game :>> ', err.message));
+          // Tell game manager a player left, if he was playing
+          if (player.isPlayingGameId >= 0) {
+            gamesManager.playerLeft(player, true);
+          }
           connectedPlayers.delete(socket.id);
           player.setConnected(false);
           player.setSocket(null);
@@ -51,9 +49,7 @@ function Server() {
         }
       }
     });
-    socket.on('connect', () => {
-      console.log('Connected to server');
-    });
+
     socket.on('auth', async (playerName, callback) => {
       console.log(' Received auth message, player name : ' + playerName);
       await updatePlayersFromDb();
@@ -100,7 +96,7 @@ function Server() {
         authorizedPlayers.get(playerName).setSocket(socket);
         connectedPlayers.set(socket.id, playerName);
       }
-
+      console.log('auth called, result : ', response.message);
       callback(response);
       if (kickout === true) {
         setTimeout(() => {
@@ -110,7 +106,6 @@ function Server() {
     });
 
     socket.on('games-list', async (playerName, callback) => {
-      console.log(' Received games-list message, player name : ' + playerName);
       let response = {};
       try {
         let games = await getJoinableGames(playerName);
@@ -138,22 +133,24 @@ function Server() {
         gamesManager.addPlayerInGame(
           authorizedPlayers.get(connectedPlayers.get(socket.id)),
           id,
-          false
+          false,
+          true,
+          { deck: game.deck, advRules: game.advRules }
         );
-        console.log('Force all clients to reload their games');
         forceClientsReloadGames();
       } catch (error) {
-        console.log('error in create-game:>> ', error);
+        console.log('In create-game catch error :>> ', error);
         response = {
           res: 'ko',
           message: 'Error from server'
         };
       }
+      console.log('create-game called, result : ', response.message);
       callback(response);
     });
 
     socket.on('join-game', async (gameDetails, callback) => {
-      let result = gamesManager.addPlayerInGame(
+      let result = await gamesManager.addPlayerInGame(
         authorizedPlayers.get(connectedPlayers.get(socket.id)),
         gameDetails.id,
         gameDetails.joinCreatedGame
@@ -171,7 +168,6 @@ function Server() {
 
     socket.on('leave-game', async (username, callback) => {
       // Remove player from game he is playing
-      gamesManager.playerLeft(authorizedPlayers.get(username), false);
       try {
         let res = removeAllGamesCreatedByPlayer(username);
         if (res.affectedRows > 0) {
@@ -180,6 +176,7 @@ function Server() {
       } catch (err) {
         console.log('Error removing games : ' + err.message);
       }
+      gamesManager.playerLeft(authorizedPlayers.get(username), false);
       callback(true);
     });
   });
@@ -188,10 +185,8 @@ function Server() {
     try {
       let usernames = await mariadbConn.getUsernames();
       authorizedPlayersNames = new Set(usernames);
-      console.log('authorizedPlayersNames update : ', authorizedPlayersNames);
       authorizedPlayersNames.forEach(name => {
         if (!authorizedPlayers.has(name)) {
-          console.log('New username authorized :', name);
           authorizedPlayers.set(name, new PlayerId(name));
         }
       });

+ 12 - 12
server/tools/server-tool-listener.js → server/src/tools/server-tool-listener.js

@@ -4,48 +4,48 @@ import io from 'socket.io';
 
 export default class ServerToolListner {
   constructor(server) {
-    this.server=server;
+    this.server = server;
   }
 
   listen(port) {
     let server = this.server;
     this.serverTool = io.listen(port);
-    this.serverTool.on('connection', function (socket) {
+    this.serverTool.on('connection', function(socket) {
       console.log('tool connected');
 
-      socket.on('players', (option,callback) =>  {
+      socket.on('players', (option, callback) => {
         callback(server.getCurrentListAuthorizedPlayers());
         socket.disconnect(false);
       });
-      socket.on('syncdb', async (option,callback) =>  {
+      socket.on('syncdb', async (option, callback) => {
         let response = await server.updatePlayersFromDb();
         callback(response);
         socket.disconnect(false);
       });
 
-      socket.on('add-player', async (username,callback) =>  {
+      socket.on('add-player', async (username, callback) => {
         let response = await server.addPlayerDb(...username);
         callback(response);
         socket.disconnect(false);
       });
-      socket.on('remove-player', async (username,callback) =>  {
+      socket.on('remove-player', async (username, callback) => {
         let response = await server.removePlayerDb(...username);
         callback(response);
         socket.disconnect(false);
       });
 
-      socket.on('add-game', async (game,callback) =>  {
-        let response='';
+      socket.on('add-game', async (game, callback) => {
+        let response = '';
         try {
           response = await server.addGameDb(...game);
-          response = "OK - inserted at id :" + response;
-        } catch(err) {
-          response= 'KO ' + err.message;
+          response = 'OK - inserted at id :' + response;
+        } catch (err) {
+          response = 'KO ' + err.message;
         }
         callback(JSON.stringify(response));
         socket.disconnect(false);
       });
-      socket.on('remove-game', async (options,callback) =>  {
+      socket.on('remove-game', async (options, callback) => {
         let response = await server.removeGameDb(...options);
         callback(response);
         socket.disconnect(false);

+ 15 - 16
server/tools/server-tools.js → server/src/tools/server-tools.js

@@ -1,7 +1,7 @@
 'use strict';
 
 // process.exit(1);
-var usage = function () {
+var usage = function() {
   var text = `
     USAGE : npm run tools <option>
       with <option> :
@@ -32,26 +32,25 @@ if (args.length === 0) {
   usage();
   process.exit(0);
 }
-var command = "";
-var option = "";
+var command = '';
+var option = '';
 switch (args[0]) {
-  case "players":
-  case "syncdb":
+  case 'players':
+  case 'syncdb':
     command = args[0];
     break;
 
-  case "add-player":
-  case "remove-player":
-  case "add-game":
-  case "remove-game":
-
-    if (args[0] === "remove-game" && args.length < 3) {
+  case 'add-player':
+  case 'remove-player':
+  case 'add-game':
+  case 'remove-game':
+    if (args[0] === 'remove-game' && args.length < 3) {
       usage();
       process.exit(0);
     }
-    if (args[1] && args[1] !== "") {
+    if (args[1] && args[1] !== '') {
       command = args[0];
-      if (args[0] === "add-game") {
+      if (args[0] === 'add-game') {
         try {
           option = [JSON.parse(args[1])];
         } catch (error) {
@@ -73,10 +72,10 @@ switch (args[0]) {
     break;
 }
 
-if (command !== "") {
+if (command !== '') {
   console.log('Connecting to server...');
-  var socket = require('socket.io-client')("http://localhost:1664");
-  socket.emit(command, option,function (response) {
+  var socket = require('socket.io-client')('http://localhost:1664');
+  socket.emit(command, option, function(response) {
     console.log('Done, response from server : ', response);
   });
 }

+ 10 - 2
src/common/heroes-display/HeroesDisplay.vue

@@ -13,6 +13,10 @@
     ></heroes-filter>
     <hr />
     <heroes-selector v-if="selectHeroes.enable"></heroes-selector>
+    <button class="btn btn-primary" @click="selectFaction">
+      Select faction
+    </button>
+    <hr />
     <div
       class="row row-cols-2 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 mt-3"
     >
@@ -32,6 +36,7 @@ import { mapGetters } from 'vuex';
 import Hero from './components/Hero';
 import HeroesFilter from './components/HeroesFilter';
 import HeroesSelector from './components/HeroesSelector';
+import { socketService } from '../../main';
 
 let initialFilter = function(filterOptions) {
   return {
@@ -62,13 +67,16 @@ export default {
   },
   computed: {
     ...mapGetters({
-      heroesBy: 'heroes/by',
-      allHeroes: 'heroes/all'
+      heroesBy: 'game/filterHeroes',
+      allHeroes: 'game/allHeroes'
     })
   },
   methods: {
     resetFilter() {
       Object.assign(this.filter, initialFilter(this.filterOptions));
+    },
+    selectFaction() {
+      socketService.endTurn();
     }
   },
   watch: {

+ 18 - 0
src/common/socket-service.js

@@ -51,6 +51,11 @@ export default function SocketService(socketEventBus, vuexStore) {
       ioClient.on('message', message => {
         store.dispatch('addMessageToQueue', message);
       });
+      ioClient.on('update-game-data', data => {
+        if (data) {
+          store.dispatch('game/update', data);
+        }
+      });
     });
     return promise;
   };
@@ -131,6 +136,18 @@ export default function SocketService(socketEventBus, vuexStore) {
       error => Promise.reject(error)
     );
   };
+  let endTurn = function(payload) {
+    return checkConnection(payload.username).then(
+      () => {
+        return new Promise(resolve => {
+          ioClient.emit('end-turn', payload, function(response) {
+            resolve(response);
+          });
+        });
+      },
+      error => Promise.reject(error)
+    );
+  };
 
   let chat = function(message) {
     ioClient.emit('chat', message);
@@ -142,6 +159,7 @@ export default function SocketService(socketEventBus, vuexStore) {
     createGame,
     leaveGame,
     joinGame,
+    endTurn,
     chat
   };
 }

+ 2 - 9
src/game/AppGame.vue

@@ -51,7 +51,6 @@
 import { globalEventsBus } from '../main';
 import { socketService } from '../main';
 import HeroesDisplay from '../common/heroes-display/HeroesDisplay';
-import { mapActions } from 'vuex';
 export default {
   components: {
     HeroesDisplay
@@ -87,20 +86,14 @@ export default {
       }
     }
   },
-  computed: {
-    ...mapActions({
-      setHeroesFromJson: 'heroes/setFromLocalJson'
-    })
-  },
+  computed: {},
   methods: {
     sendChat() {
       socketService.chat(this.chatMessage);
       this.chatMessage = '';
     }
   },
-  created() {
-    this.setHeroesFromJson.then();
-  }
+  created() {}
 };
 </script>
 

+ 1 - 1
src/menu/game-creation/MenuGameCreation.vue

@@ -81,7 +81,7 @@ export default {
         deck: this.selectedDeck,
         advRules: this.advRules,
         status: 'CREATED',
-        data: ''
+        data: '{}'
       };
       socketService
         .createGame(game)

+ 0 - 28
src/server-shared/heroesHelper.js

@@ -1,28 +0,0 @@
-export const initHeroesFromJson = function(allHeroesJson) {
-  let abilitiesMap = new Map();
-  allHeroesJson.abilities.forEach(ability => {
-    abilitiesMap.set(ability.abilityName, {
-      name: ability.abilityName,
-      hook: ability.abilityHook,
-      isOptionnal: ability.optionnal,
-      desc: ability['abilityDesc-FR']
-    });
-  });
-  let heroesSet = new Set();
-  allHeroesJson.heroes.forEach(hero => {
-    let i = 0;
-    while (i < hero.nbInDeck) {
-      heroesSet.add({
-        name: hero.name,
-        cost: hero.cost,
-        power: hero.power,
-        faction: hero.faction,
-        ability: abilitiesMap.get(hero.ability),
-        isDraftable: hero.draftMode,
-        popularity: hero.popularity
-      });
-      i++;
-    }
-  });
-  return heroesSet;
-};

+ 132 - 0
src/store/game/game.js

@@ -0,0 +1,132 @@
+import AnyPlayer from './player/any-player';
+import allHeroesJson from '../../../server/src/client-server-shared/all-heroes.json';
+
+const state = {
+  gameState: '',
+  allHeroes: [],
+  deckMode: '',
+  advRules: [],
+  'waitingFor/blue': false,
+  'waitingFor/red': false,
+  currentPlayer: '',
+  'battleTiles/left': [],
+  'battleTiles/center': [],
+  'battleTiles/right': [],
+  totalFood: 0,
+  allHeroesJson
+};
+
+const getters = {
+  state(state) {
+    return state.gameState;
+  },
+  allHeroes(state) {
+    return state.allHeroes;
+  },
+  heroById: state => id => {
+    return state.allHeroes.find(hero => hero.id === id);
+  },
+  filterHeroes: state => filter => {
+    console.log('filter :>> ', filter);
+    // filter has
+    //  - faction (orcs humans elves meca none all)
+    //  - popularity (with without)
+    //  - draft (yes no all)
+    //  -  minPower <= power <= maxPower
+    //  -  minCost <= cost <= maxCost
+    //  - byName : if true filter name with filter.name
+    //  - byId : [] of ids, deactivated if null or empty.
+    if (!filter.faction) filter.faction = 'all';
+    if (!filter.popularity) filter.popularity = 'without';
+    if (!filter.minPower) filter.minPower = 0;
+    if (!filter.maxPower) filter.maxPower = 0;
+    if (!filter.minCost) filter.minCost = 0;
+    if (!filter.maxCost) filter.minCost = 0;
+
+    let draftFilter = 'all';
+    switch (filter.draft) {
+      case 'yes':
+        draftFilter = true;
+        break;
+      case 'no':
+        draftFilter = false;
+        break;
+
+      default:
+        draftFilter = 'all';
+        break;
+    }
+    console.log('state.allHeroes :>> ', state.allHeroes);
+    return state.allHeroes.filter(hero => {
+      if (filter.faction !== 'all' && hero.faction !== filter.faction)
+        return false;
+      if (hero.popularity !== 'any' && hero.popularity !== filter.popularity)
+        return false;
+
+      if (draftFilter !== 'all' && hero.isDraftable !== draftFilter)
+        return false;
+
+      if (hero.power < filter.minPower || hero.power > filter.maxPower)
+        return false;
+      if (hero.cost < filter.minCost || hero.cost > filter.maxCost)
+        return false;
+
+      if (
+        filter.byName === true &&
+        !hero.name.toLowerCase().includes(filter.name.toLowerCase())
+      )
+        return false;
+
+      if (
+        filter.byId &&
+        filter.byId.length > 0 &&
+        filter.byId.includes(hero.id)
+      ) {
+        console.log('not returning hero :>> ', hero);
+        console.log('filter.byId :>> ', filter.byId);
+        return false;
+      }
+      console.log('returning hero :>> ', hero);
+      return true;
+    });
+  }
+};
+
+const mutations = {
+  SET_HEROES: (state, payload) => {
+    state.allHeroes = payload;
+  },
+  SET_FULL_GAME_STATE: (state, payload) => {
+    Object.assign(state, payload);
+  }
+};
+
+const actions = {
+  setHeroes: ({ commit }, payload) => {
+    commit('SET_HEROES', payload);
+  },
+  update: ({ commit }, payload) => {
+    commit('SET_FULL_GAME_STATE', payload.game);
+    commit('bluePlayer/SET_FULL_PLAYER_STATE', payload.bluePlayer);
+    commit('redPlayer/SET_FULL_PLAYER_STATE', payload.redPlayer);
+  }
+};
+const bluePlayer = new AnyPlayer();
+const redPlayer = new AnyPlayer();
+export default {
+  namespaced: true,
+  state,
+  getters,
+  mutations,
+  actions,
+  modules: {
+    bluePlayer: {
+      namespaced: true,
+      ...bluePlayer
+    },
+    redPlayer: {
+      namespaced: true,
+      ...redPlayer
+    }
+  }
+};

+ 44 - 0
src/store/game/player/any-player.js

@@ -0,0 +1,44 @@
+export default function AnyPlayer() {
+  const state = {
+    // Username of this player
+    name: '',
+    // Color of this player (blue or red)
+    color: '',
+    // Faction chosen (will stay empty if not faction mode)
+    faction: '',
+    // Will contain the IDs of the heroes selectable for draft mode
+    draftHeroesIds: [],
+    // the 12 heroes the player is playing with
+    // Player will fill it himself if mode is draft or tournament
+    // contains object : {id , position , possibleActions :[]}
+    twelveHeroes: [],
+    foodInCamp: 0,
+    // Can see already we might have issues with left/right depending from where we look :)
+    'foodInBattle/left': 0,
+    'foodInBattle/center': 0,
+    'foodInBattle/right': 0,
+    // During military phase it will contain actions made by player to be replayed by other
+    actionsPerformed: []
+  };
+
+  const getters = {
+    color: state => {
+      return state.color;
+    }
+  };
+
+  const mutations = {
+    SET_FULL_PLAYER_STATE: (state, payload) => {
+      Object.assign(state, payload);
+    }
+  };
+
+  const actions = {};
+  return {
+    namespace: true,
+    state,
+    getters,
+    mutations,
+    actions
+  };
+}

+ 0 - 85
src/store/heroes/heroes.js

@@ -1,85 +0,0 @@
-import { initHeroesFromJson } from '../../server-shared/heroesHelper';
-import allHeroesJson from '../../server-shared/all-heroes.json';
-const state = {
-  heroes: new Set()
-};
-
-const getters = {
-  all(state) {
-    return state.heroes;
-  },
-  by: state => filter => {
-    // filter has
-    //  - faction (orcs humans elves meca none all)
-    //  - popularity (with without)
-    //  - draft (yes no all)
-    //  -  minPower <= power <= maxPower
-    //  -  minCost <= cost <= maxCost
-    //  - byName : if true filter name with filter.name
-    if (!filter.faction) filter.faction = 'all';
-    if (!filter.popularity) filter.popularity = 'without';
-    if (!filter.minPower) filter.minPower = 0;
-    if (!filter.maxPower) filter.maxPower = 0;
-    if (!filter.minCost) filter.minCost = 0;
-    if (!filter.maxCost) filter.minCost = 0;
-
-    let draftFilter = 'all';
-    switch (filter.draft) {
-      case 'yes':
-        draftFilter = true;
-        break;
-      case 'no':
-        draftFilter = false;
-        break;
-
-      default:
-        draftFilter = 'all';
-        break;
-    }
-
-    return Array.from(state.heroes).filter(hero => {
-      if (filter.faction !== 'all' && hero.faction !== filter.faction)
-        return false;
-      if (hero.popularity !== 'any' && hero.popularity !== filter.popularity)
-        return false;
-
-      if (draftFilter !== 'all' && hero.isDraftable !== draftFilter)
-        return false;
-
-      if (hero.power < filter.minPower || hero.power > filter.maxPower)
-        return false;
-      if (hero.cost < filter.minCost || hero.cost > filter.maxCost)
-        return false;
-
-      if (
-        filter.byName === true &&
-        !hero.name.toLowerCase().includes(filter.name.toLowerCase())
-      )
-        return false;
-
-      return true;
-    });
-  }
-};
-
-const mutations = {
-  SET_HEROES: (state, payload) => {
-    state.heroes = payload;
-  }
-};
-
-const actions = {
-  setFromLocalJson: ({ commit }) => {
-    commit('SET_HEROES', initHeroesFromJson(allHeroesJson));
-  },
-  setAvailableHeroes: ({ commit }, payload) => {
-    commit('SET_HEROES', payload);
-  }
-};
-export default {
-  namespaced: true,
-  state,
-  getters,
-  mutations,
-  actions
-};

+ 28 - 0
src/store/menu/menu.js

@@ -0,0 +1,28 @@
+const state = {
+  joinableGames: []
+};
+
+const getters = {
+  joinableGames(state) {
+    return state.joinableGames;
+  }
+};
+
+const mutations = {
+  SET_JOIGNABLE_GAMES: (state, payload) => {
+    state.joinableGames = payload;
+  }
+};
+
+const actions = {
+  setJoignableGames: ({ commit }, payload) => {
+    commit('SET_JOIGNABLE_GAMES', payload);
+  }
+};
+export default {
+  namespaced: true,
+  state,
+  getters,
+  mutations,
+  actions
+};

+ 5 - 2
src/store/store.js

@@ -4,7 +4,8 @@ import Vuex from 'vuex';
 
 Vue.use(Vuex);
 
-import heroes from './heroes/heroes';
+import menu from './menu/menu';
+import game from './game/game';
 
 const state = {
   username: '',
@@ -28,6 +29,7 @@ const actions = {
     commit('ADD_MESSAGE', payload);
   }
 };
+console.log('menu :>> ', menu);
 export const store = new Vuex.Store({
   state,
   getters,
@@ -35,6 +37,7 @@ export const store = new Vuex.Store({
   actions,
 
   modules: {
-    heroes
+    menu,
+    game
   }
 });

+ 23 - 0
src/store/types.js

@@ -0,0 +1,23 @@
+'use strict';
+
+export default {
+  // Positions of heroes in game
+  POS_PILE: 'pile',
+  POS_HAND: 'hand',
+  POS_DISCARD: 'discard',
+  POS_CAMP: 'camp',
+  POS_BATTLE_LEFT: 'battle_left',
+  POS_BATTLE_CENTER: 'battle_center',
+  POS_BATTLE_RIGHT: 'battle_right',
+
+  // Possible hero actions in game
+  HERO_RECRUIT: 'recruit',
+  HERO_DEPLOY: 'deploy',
+  HERO_MOVE: 'move',
+  HERO_ABILITY: 'ability',
+  HERO_DISMISS: 'dismiss',
+  HERO_DISCARD: 'discard',
+
+  // Possible actions for player in game
+  PLAYER_SUPPLY: 'supply'
+};

Some files were not shown because too many files changed in this diff