This commit is contained in:
Raphaël Jakse 2020-04-04 16:27:18 +02:00
parent a1a81f58f2
commit f59e56bd62
4 changed files with 685 additions and 318 deletions

317
.eslintrc.json Normal file
View File

@ -0,0 +1,317 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"accessor-pairs": "error",
"array-bracket-newline": "off",
"array-bracket-spacing": [
"error",
"never"
],
"array-callback-return": "error",
"array-element-newline": "off",
"arrow-body-style": "error",
"arrow-parens": "error",
"arrow-spacing": "error",
"block-scoped-var": "off",
"block-spacing": [
"error",
"never"
],
"brace-style": [
"error",
"1tbs",
{
"allowSingleLine": true
}
],
"callback-return": "error",
"camelcase": "error",
"capitalized-comments": [
"error",
"always"
],
"class-methods-use-this": "error",
"comma-dangle": "error",
"comma-spacing": [
"error",
{
"after": true,
"before": false
}
],
"comma-style": [
"error",
"last"
],
"complexity": "off",
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": "off",
"consistent-this": "error",
"curly": "error",
"default-case": "off",
"default-param-last": "error",
"dot-location": "error",
"dot-notation": "error",
"eol-last": "error",
"eqeqeq": "error",
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "off",
"func-style": [
"error",
"declaration"
],
"function-call-argument-newline": "off",
"function-paren-newline": "off",
"generator-star-spacing": "error",
"global-require": "error",
"grouped-accessor-pairs": "error",
"guard-for-in": "error",
"handle-callback-err": "error",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"implicit-arrow-linebreak": "error",
"indent": "off",
"indent-legacy": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "off",
"keyword-spacing": [
"error", {
"before": true,
"after": true
}
],
"line-comment-position": "off",
"linebreak-style": [
"error",
"unix"
],
"lines-around-comment": "error",
"lines-around-directive": "error",
"lines-between-class-members": "error",
"max-classes-per-file": "error",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-lines-per-function": "off",
"max-nested-callbacks": "error",
"max-params": "error",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-comment-style": "error",
"new-cap": "error",
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "error",
"no-alert": "error",
"no-array-constructor": "error",
"no-await-in-loop": "error",
"no-bitwise": "error",
"no-buffer-constructor": "error",
"no-caller": "error",
"no-catch-shadow": "error",
"no-confusing-arrow": "error",
"no-console": "off",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-constructor-return": "error",
"no-continue": "error",
"no-div-regex": "error",
"no-dupe-else-if": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty-function": "error",
"no-eq-null": "error",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-import-assign": "error",
"no-inline-comments": "off",
"no-inner-declarations": [
"error",
"functions"
],
"no-invalid-this": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "off",
"no-magic-numbers": "off",
"no-mixed-operators": "error",
"no-mixed-requires": "error",
"no-multi-assign": "off",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "error",
"no-native-reassign": "off",
"no-negated-condition": "error",
"no-negated-in-lhs": "error",
"no-nested-ternary": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "off",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-setter-return": "error",
"no-shadow": "off",
"no-spaced-func": "error",
"no-sync": "error",
"no-tabs": "error",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "error",
"no-undef-init": "error",
"no-undefined": "error",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unused-expressions": "error",
"no-use-before-define": "off",
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "off",
"no-void": "error",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"nonblock-statement-body-position": "error",
"object-curly-newline": "error",
"object-curly-spacing": [
"error",
"never"
],
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": [
"error",
"always"
],
"operator-linebreak": "error",
"padded-blocks": "off",
"padding-line-between-statements": "error",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"prefer-destructuring": "off",
"prefer-exponentiation-operator": "error",
"prefer-named-capture-group": "error",
"prefer-numeric-literals": "error",
"prefer-object-spread": "error",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-regex-literals": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": [
"error",
"as-needed"
],
"require-atomic-updates": "error",
"require-await": "error",
"require-jsdoc": "off",
"require-unicode-regexp": "error",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": [
"error",
{
"after": true,
"before": false
}
],
"semi-style": [
"error",
"last"
],
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "error",
"space-before-function-paren": "off",
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "off",
"space-unary-ops": "error",
"spaced-comment": "off",
"strict": [
"error",
"function"
],
"switch-colon-spacing": "error",
"symbol-description": "error",
"template-curly-spacing": "error",
"template-tag-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "error",
"vars-on-top": "off",
"wrap-iife": [
"error",
"any"
],
"wrap-regex": "error",
"yield-star-spacing": "error",
"yoda": [
"error",
"never"
]
}
}

View File

@ -1,32 +1,46 @@
(function (that) {
var divAlert, divAlertInput, divAlertConfirm, divAlertButton, alertButtonOK,
divAlertCallback, divAlertCallbackYes, divAlertCallbackNo, alertInput;
/*global libD*/
var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) { return s; };
(function (global) {
"use strict";
var divAlert, divAlertInput, divAlertConfirm, divAlertButton, alertButtonOK,
divAlertCallback, divAlertCallbackYes, divAlertCallbackNo, alertInput,
divAlertContent;
var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {return s;};
function promptOK() {
divAlert.style.display = "none";
divAlertCallback && divAlertCallback(alertInput.value);
if (divAlertCallback) {
divAlertCallback(alertInput.value);
}
}
function promptCancel() {
divAlert.style.display = "none";
divAlertCallback && divAlertCallback(null);
if (divAlertCallback) {
divAlertCallback(null);
}
}
function confirmYes() {
divAlert.style.display = "none";
divAlertCallbackYes && divAlertCallbackYes();
if (divAlertCallbackYes) {
divAlertCallbackYes();
}
}
function confirmNo() {
divAlert.style.display = "none";
divAlertCallbackNo && divAlertCallbackNo();
if (divAlertCallbackNo) {
divAlertCallbackNo();
}
}
function alertOK() {
divAlert.style.display = "none";
divAlertCallback && divAlertCallback();
if (divAlertCallback) {
divAlertCallback();
}
}
function prepare() {
@ -86,7 +100,7 @@
document.body.appendChild(divAlert);
}
that.myAlert = function (msg, callback) {
global.myAlert = function (msg, callback) {
if (!divAlert) {
prepare();
}
@ -100,9 +114,9 @@
divAlertButton.getElementsByTagName("button")[0].focus();
};
that.myAlert.l10n = _;
global.myAlert.l10n = _;
that.myPrompt = function (msg, callback, defaultText) {
global.myPrompt = function (msg, callback, defaultText) {
if (!divAlert) {
prepare();
}
@ -115,14 +129,14 @@
divAlertCallback = callback;
divAlert.style.display = "";
if (!/iPad|iPhone|iPod/g.test(navigator.userAgent)) {
if (!(/iPad|iPhone|iPod/gu).test(navigator.userAgent)) {
alertInput.focus();
alertInput.setSelectionRange(0, alertInput.value.length);
alertInput.select();
}
};
that.myConfirm = function (msg, callbackYes, callbackNo) {
global.myConfirm = function (msg, callbackYes, callbackNo) {
if (!divAlert) {
prepare();
}

View File

@ -1,3 +1,4 @@
/* eslint-disable no-process-env */
/**
* Copyright (C) 2016-2020 Raphaël Jakse <raphael.trivabble@jakse.fr>
*
@ -22,35 +23,44 @@
* @source: https://gitlab.com/raphj/trivabble/
*/
const port = 3000;
/*eslint strict: [2, "global"]*/
"use strict";
const port = parseInt(process.env.TRIVABBLE_PORT || "3000");
const SAVE_TIMEOUT = 5000;
const KEEP_ALIVE = 30000;
const DEV_ENABLE_SERVING_FILES = (process.env.DEV_ENABLE_SERVING_FILES || "").toLowerCase() === "true";
if (DEV_ENABLE_SERVING_FILES) {
console.log("DEV_ENABLE_SERVING_FILES: Serving files in the current directory. Please never do this on a production server, this is for development purposes only.");
}
var http = require("http");
var fs = require("fs");
var frBag = [
" ", " ", // jokers
"E","E","E","E","E","E","E","E","E","E","E","E","E","E","E",
"A","A","A","A","A","A","A","A","A",
"I","I","I","I","I","I","I","I",
"N","N","N","N","N","N",
"O","O","O","O","O","O",
"R","R","R","R","R","R",
"S","S","S","S","S","S",
"T","T","T","T","T","T",
"U","U","U","U","U","U",
"L","L","L","L","L",
"D","D","D",
"M","M","M",
"G","G",
"B","B",
"C","C",
"P","P",
"F","F",
"H","H",
"V","V",
" ", " ", // Jokers
"E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", "E",
"A", "A", "A", "A", "A", "A", "A", "A", "A",
"I", "I", "I", "I", "I", "I", "I", "I",
"N", "N", "N", "N", "N", "N",
"O", "O", "O", "O", "O", "O",
"R", "R", "R", "R", "R", "R",
"S", "S", "S", "S", "S", "S",
"T", "T", "T", "T", "T", "T",
"U", "U", "U", "U", "U", "U",
"L", "L", "L", "L", "L",
"D", "D", "D",
"M", "M", "M",
"G", "G",
"B", "B",
"C", "C",
"P", "P",
"F", "F",
"H", "H",
"V", "V",
"J",
"Q",
"K",
@ -88,7 +98,7 @@ var frValues = {
"X": 10,
"Y": 10,
"Z": 10
}
};
var games = {};
@ -97,9 +107,9 @@ var dateNow = 0;
var saveTo = null;
function saveGames() {
fs.writeFile('games.backup.json', JSON.stringify(games), function (err) {
fs.writeFile("games.backup.json", JSON.stringify(games), function (err) {
if (err) {
console.error('ERROR: Cannot save games!');
console.error("ERROR: Cannot save games!");
}
});
@ -158,7 +168,7 @@ Game.prototype.init = function () {
this.remainingLetters = this.bag.length;
this.racks = {};
this.scores = {};
}
};
Game.prototype.toJSON = function () {
return {
@ -176,7 +186,7 @@ Game.fromJSON = function (obj) {
game.bag = obj.bag || frBag.slice();
game.remainingLetters = obj.remainingLetters || game.bag.length;
game.racks = obj.racks || {};
game.scores = obj.scores || {}
game.scores = obj.scores || {};
return game;
};
@ -193,7 +203,7 @@ Game.prototype.getPlayerScore = function (player) {
Game.prototype.setPlayerScore = function (player, score) {
var playerID = "#" + player;
if (!this.racks.hasOwnProperty(playerID) || typeof score !== "number") {
if (!Object.prototype.hasOwnProperty.call(this.racks, playerID) || typeof score !== "number") {
return;
}
@ -212,13 +222,13 @@ Game.prototype.setPlayerScore = function (player, score) {
Game.prototype.playerJoined = function (playerName) {
if (playerName) {
this.getPlayerRack(playerName); // create the player's rack
this.getPlayerRack(playerName); // Create the player's rack
}
var players = [];
for (var player in this.racks) {
if (this.racks.hasOwnProperty(player)) {
if (Object.prototype.hasOwnProperty.call(this.racks, player)) {
player = player.slice(1); // '#'
players.push(
{
@ -260,12 +270,12 @@ Game.prototype.addListeningPlayer = function (playerName, responseAndIsES) {
Game.prototype.commit = function () {
var pendingEvents = this.pendingEvents;
this.pendingEvents = [];
msg = JSON.stringify(pendingEvents);
var msg = JSON.stringify(pendingEvents);
for (var i = 0; i < this.listeningPlayers.length; i++) {
while (i < this.listeningPlayers.length && !this.listeningPlayers[i]) {
this.listeningPlayers[i] = this.listeningPlayers[this.listeningPlayers.length - 1];
that.listeningPlayers.pop();
this.listeningPlayers.pop();
}
if (this.listeningPlayers[i]) {
@ -281,7 +291,8 @@ Game.prototype.commit = function () {
Game.prototype.bagPopLetter = function (player) {
if (this.remainingLetters) {
var letter = "";
var index
var index;
while (letter === "") {
index = Math.floor(Math.random() * this.bag.length);
letter = this.bag[index];
@ -289,14 +300,13 @@ Game.prototype.bagPopLetter = function (player) {
this.bag[index] = "";
this.remainingLetters--;
this.pendingEvents.push(
{
player: player,
action: "popBag",
remainingLetters: this.remainingLetters,
date: dateNow
}
);
this.pendingEvents.push({
player: player,
action: "popBag",
remainingLetters: this.remainingLetters,
date: dateNow
});
return letter;
}
@ -305,7 +315,7 @@ Game.prototype.bagPopLetter = function (player) {
Game.prototype.getCell = function (index) {
return this.board[index];
}
};
Game.prototype.setCell = function (index, letter, player) {
this.board[index] = letter;
@ -345,16 +355,14 @@ Game.prototype.bagPushLetter = function (letter, player) {
Game.prototype.reset = function (player) {
this.init();
this.pendingEvents.push(
{
player: player,
action: "reset",
board: this.board,
remainingLetters: this.remainingLetters,
rack: [],
date: dateNow
}
);
this.pendingEvents.push({
player: player,
action: "reset",
board: this.board,
remainingLetters: this.remainingLetters,
rack: [],
date: dateNow
});
this.playerJoined();
};
@ -379,12 +387,6 @@ function newGameId() {
return number.toString();
}
destToAction = {
"rack": "setRackCell",
"board": "setCell",
"bag": "pushBag"
}
function countTiles(rack) {
var count = 0;
@ -401,246 +403,236 @@ function handleCommand(cmd, gameNumber, playerName, response) {
var game = games[gameNumber];
if (!game && (cmd.cmd !== "joinGame")) {
response.write('{"error":1,"reason":"Missing or bad game number"}');
response.write("{\"error\":1,\"reason\":\"Missing or bad game number\"}");
return;
}
var rack = null;
switch (cmd.cmd) {
case "joinGame":
if (!gameNumber) {
gameNumber = newGameId();
}
case "joinGame":
if (!gameNumber) {
gameNumber = newGameId();
}
if (!game) {
game = games[gameNumber] = new Game();
}
if (!game) {
game = games[gameNumber] = new Game();
}
game.playerJoined(playerName);
game.playerJoined(playerName);
response.write(
JSON.stringify(
{
"error":0,
"gameNumber": gameNumber,
"playerName": playerName,
"rack": game.getPlayerRack(playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"letterValues": frValues,
"date": dateNow,
"sync": false
}
)
response.write(
JSON.stringify({
"error":0,
"gameNumber": gameNumber,
"playerName": playerName,
"rack": game.getPlayerRack(playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"letterValues": frValues,
"date": dateNow,
"sync": false
})
);
break;
break;
case "hello":
game.playerJoined(playerName);
break;
case "hello":
game.playerJoined(playerName);
break;
case "sync":
response.write(
JSON.stringify(
{
"error":0,
"gameNumber": gameNumber,
"playerName": playerName,
"rack": game.getPlayerRack(playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"letterValues": frValues,
"date": dateNow,
"sync": true
}
)
);
break;
case "sync":
response.write(
JSON.stringify({
"error":0,
"gameNumber": gameNumber,
"playerName": playerName,
"rack": game.getPlayerRack(playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"letterValues": frValues,
"date": dateNow,
"sync": true
})
);
break;
case "score":
game.setPlayerScore(cmd.player, cmd.score);
response.write('{"error":0}');
break;
case "score":
game.setPlayerScore(cmd.player, cmd.score);
response.write("{\"error\":0}");
break;
case "moveLetter":
var letter = "";
case "moveLetter":
var letter = "";
switch (cmd.from) {
case "rack":
rack = game.getPlayerRack(playerName);
letter = rack[cmd.indexFrom];
rack[cmd.indexFrom] = "";
updateRack = true;
break;
switch (cmd.from) {
case "rack":
rack = game.getPlayerRack(playerName);
case "board":
if (cmd.indexFrom < 15 * 15) {
letter = game.getCell(cmd.indexFrom);
game.setCell(cmd.indexFrom, "", playerName);
}
break;
case "bag":
letter = game.bagPopLetter(playerName);
break;
default:
response.write('{"error":1, "reason":"Moving letter from an unknown place"}');
return;
}
switch (cmd.to) {
case "rack":
if (cmd.indexTo > 7) {
response.write('{"error":1, "reason":"Moving letter to a index which is too high"}');
return;
}
rack = rack || game.getPlayerRack(playerName);
rack[cmd.indexTo] = letter;
break;
case "board":
if (cmd.indexTo < 15 * 15) {
game.setCell(cmd.indexTo, letter, playerName);
}
break;
case "bag":
game.bagPushLetter(letter, playerName);
break;
default:
switch (cmd.from) {
case "rack":
rack[cmd.indexFrom] = letter;
break;
case "board":
game.setCell(cmd.indexFrom, letter, playerName);
break;
case "bag":
game.bagPushLetter(letter, playerName);
break;
default:
console.error("BUG: code should not have been reached.");
}
response.write('{"error":1, "reason":"Moving letter to an unknown place"}');
return;
}
response.write(
JSON.stringify(
{
error:0,
letter: letter,
action: "moveLetter",
indexTo: typeof cmd.indexTo === "number" ? cmd.indexTo : -1,
from: cmd.from,
to: cmd.to,
indexFrom: typeof cmd.indexFrom === "number" ? cmd.indexFrom : -1,
remainingLetters: game.remainingLetters,
date: dateNow
}
)
);
if (rack) {
game.pendingEvents.push(
{
players: [{
player: playerName,
rackCount: countTiles(rack)
}],
date: dateNow
}
);
}
break;
case "setRack":
cmd.rack.length = 8;
var rack = game.getPlayerRack(playerName);
var initialRackCount = countTiles(rack);
var rackCount = countTiles(cmd.rack);
console.log(rack, cmd.rack);
if (initialRackCount !== rackCount) {
response.write(
JSON.stringify({
error: 1,
rack: rack,
reason: "the new rack doesn't contain the same number of letters",
date: dateNow
})
);
letter = rack[cmd.indexFrom];
rack[cmd.indexFrom] = "";
break;
}
var oldRack = rack.slice();
for (var i = 0; i < 8; i++) {
cmd.rack[i] = (cmd.rack[i] || "")[0] || "";
if (cmd.rack[i]) {
var indexLetter = oldRack.indexOf(cmd.rack[i]);
if (indexLetter === -1) {
response.write(
JSON.stringify({
error: 1,
rack: rack,
reason: "the new rack is not a permutation of the old rack",
date: dateNow
})
);
break;
}
oldRack[indexLetter] = false;
case "board":
if (cmd.indexFrom < 15 * 15) {
letter = game.getCell(cmd.indexFrom);
game.setCell(cmd.indexFrom, "", playerName);
}
}
break;
for (var i = 0; i < 8; i++) {
rack[i] = cmd.rack[i];
}
case "bag":
letter = game.bagPopLetter(playerName);
break;
default:
response.write("{\"error\":1, \"reason\":\"Moving letter from an unknown place\"}");
return;
}
switch (cmd.to) {
case "rack":
if (cmd.indexTo > 7) {
response.write("{\"error\":1, \"reason\":\"Moving letter to a index which is too high\"}");
return;
}
rack = rack || game.getPlayerRack(playerName);
rack[cmd.indexTo] = letter;
break;
case "board":
if (cmd.indexTo < 15 * 15) {
game.setCell(cmd.indexTo, letter, playerName);
}
break;
case "bag":
game.bagPushLetter(letter, playerName);
break;
default:
switch (cmd.from) {
case "rack":
rack[cmd.indexFrom] = letter;
break;
case "board":
game.setCell(cmd.indexFrom, letter, playerName);
break;
case "bag":
game.bagPushLetter(letter, playerName);
break;
default:
console.error("BUG: code should not have been reached.");
}
response.write("{\"error\":1, \"reason\":\"Moving letter to an unknown place\"}");
return;
}
response.write(
JSON.stringify({
error:0,
letter: letter,
action: "moveLetter",
indexTo: typeof cmd.indexTo === "number" ? cmd.indexTo : -1,
from: cmd.from,
to: cmd.to,
indexFrom: typeof cmd.indexFrom === "number" ? cmd.indexFrom : -1,
remainingLetters: game.remainingLetters,
date: dateNow
})
);
if (rack) {
game.pendingEvents.push({
players: [{
player: playerName,
rackCount: countTiles(rack)
}],
date: dateNow
});
}
break;
case "setRack":
cmd.rack.length = 8;
rack = game.getPlayerRack(playerName);
var initialRackCount = countTiles(rack);
var rackCount = countTiles(cmd.rack);
console.log(rack, cmd.rack);
if (initialRackCount !== rackCount) {
response.write(
JSON.stringify({
error: 0,
error: 1,
rack: rack,
reason: "the new rack doesn't contain the same number of letters",
date: dateNow
})
);
break;
case "resetGame":
game.reset();
response.write('{"error":0}');
break;
}
case "msg":
game.pendingEvents.push(
{
msg: {
sender: playerName,
content: cmd.msg
},
date: dateNow
var oldRack = rack.slice();
for (let i = 0; i < 8; i++) {
cmd.rack[i] = (cmd.rack[i] || "")[0] || "";
if (cmd.rack[i]) {
var indexLetter = oldRack.indexOf(cmd.rack[i]);
if (indexLetter === -1) {
response.write(
JSON.stringify({
error: 1,
rack: rack,
reason: "the new rack is not a permutation of the old rack",
date: dateNow
})
);
break;
}
);
response.write('{"error":0}');
break;
oldRack[indexLetter] = false;
}
}
default:
response.write('{"error":1,"reason":"Unknown command"}');
break;
for (let i = 0; i < 8; i++) {
rack[i] = cmd.rack[i];
}
response.write(
JSON.stringify({
error: 0,
rack: rack,
date: dateNow
})
);
break;
case "resetGame":
game.reset();
response.write("{\"error\":0}");
break;
case "msg":
game.pendingEvents.push({
msg: {
sender: playerName,
content: cmd.msg
},
date: dateNow
});
response.write("{\"error\":0}");
break;
default:
response.write("{\"error\":1,\"reason\":\"Unknown command\"}");
break;
}
}
@ -654,15 +646,15 @@ function handleCommands(cmds, responseAndIsES) {
}
writeResponse(responseAndIsES,
"playerName": cmds.playerName,
"gameNumber": cmds.gameNumber,
"letterValues": game.letterValues,
"rack": game.getPlayerRack(cmds.playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"date": dateNow
}
)
JSON.stringify({
"playerName": cmds.playerName,
"gameNumber": cmds.gameNumber,
"letterValues": game.letterValues,
"rack": game.getPlayerRack(cmds.playerName),
"board": game.board,
"remainingLetters": game.remainingLetters,
"date": dateNow
})
);
game.addListeningPlayer(cmds.playerName, responseAndIsES);
@ -689,9 +681,9 @@ function handleCommands(cmds, responseAndIsES) {
}
function handleRequest(request, response) {
var post = '';
var post = "";
// thx http://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
// Thx http://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js
request.on("data", function (data) {
post += data;
@ -702,6 +694,50 @@ function handleRequest(request, response) {
});
request.on("end", function () {
if (DEV_ENABLE_SERVING_FILES && !request.url.startsWith("/:")) {
if (request.url === "/") {
request.url = "/index.html";
}
console.log("Serving " + request.url);
fs.exists("." + request.url, function (exists) {
if (exists) {
fs.readFile("." + request.url, function(err, contents) {
if (err) {
response.statusCode = 500;
response.setHeader("Content-Type", "text/plain; charset=utf-8");
response.end(err);
}
var mime = "application/xhtml+xml; charset=utf-8";
var mimes = {
".mp3": "audio/mpeg",
".ogg": "audio/ogg",
".js": "application/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".svg": "image/svg+xml"
};
for (var i in mimes) {
if (request.url.endsWith(i)) {
mime = mimes[i];
break;
}
}
response.setHeader("Content-Type", mime);
response.end(contents);
});
} else {
response.statusCode = 404;
response.end("404");
}
});
return;
}
var isEventSource = false;
var eventSourceMatch = request.url.match(/\/:[^/]+\/sse\/(?<data>[\s\S]+)$/u);
@ -720,7 +756,7 @@ function handleRequest(request, response) {
});
}
fs.readFile('games.backup.json', function (err, data) {
fs.readFile("games.backup.json", function (err, data) {
try {
if (err) {
console.error("WARNING: Could not restore previous backup of the games");
@ -728,7 +764,9 @@ fs.readFile('games.backup.json', function (err, data) {
var backup = JSON.parse(data);
for (var gameNumber in backup) {
games[gameNumber] = Game.fromJSON(backup[gameNumber]);
if (Object.prototype.hasOwnProperty.call(backup, gameNumber)) {
games[gameNumber] = Game.fromJSON(backup[gameNumber]);
}
}
}
} catch (e) {
@ -740,4 +778,3 @@ fs.readFile('games.backup.json', function (err, data) {
console.log("Server listening on: http://localhost:%s", port);
});
});

View File

@ -22,6 +22,8 @@
* @source: https://gitlab.com/raphj/trivabble/
*/
/*global libD, myConfirm, myAlert, myPrompt*/
(function () {
"use strict";
@ -29,13 +31,13 @@
var _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) { return s; };
window.trivabble = {l10n: _};
var trivabble = window.trivabble = {l10n: _};
function format(s, v) {
return s.replace("{0}", v);
}
var newGameBtn, board, rack, boardCells = [], scoreOf, bag;
var board, rack, boardCells = [], scoreOf, bag;
var playerLetters = [];
@ -43,18 +45,13 @@
var chatMessages, helpBag, helpClear;
var tablePlayers = {};
var participantPlaceholder, participants;
var participantPlaceholder, participants, name;
var lastDate = 0;
var blockMove = 0;
var gameInProgress = false;
function onclick(ele, fun) {
ele.addEventListener("click", fun, false);
ele.addEventListener("touch", fun, false);
}
function mouseDown(ele, fun, stop) {
var meth = stop ? "removeEventListener" : "addEventListener";
ele[meth]("mousedown", fun, false);
@ -116,7 +113,7 @@
return false;
}
function dragTileEnd(e) {
function dragTileEnd() {
movingTile.style.left = "";
movingTile.style.top = "";
movingTile.style.width = "";
@ -185,7 +182,6 @@
moveCMD = null;
}
var DBG = 0;
function dragTileMove(e) {
var newLeft = (tileInitCoords.left + (e.clientX - tileInitMouseCoords.clientX));
var newTop = (tileInitCoords.top + (e.clientY - tileInitMouseCoords.clientY));
@ -427,7 +423,7 @@
function set(key, value) {
switch (key) {
case "playerName":
document.getElementById("name").textContent = localStorage.trivabblePlayerName = value;
name.textContent = localStorage.trivabblePlayerName = value;
break;
case "gameNumber":
document.getElementById("number").textContent = localStorage.trivabbleGameNumber = value;
@ -577,13 +573,13 @@
tablePlayers[playerName] = row;
}
if (player.hasOwnProperty("score")) {
if (Object.prototype.hasOwnProperty.call(player, "score")) {
var scoreCell = tablePlayers[playerName].childNodes[2];
scoreCell.textContent = player.score;
blink(scoreCell);
}
if (player.hasOwnProperty("rackCount")) {
if (Object.prototype.hasOwnProperty.call(player, "rackCount")) {
var countCell = tablePlayers[playerName].childNodes[1];
countCell.textContent = player.rackCount;
blink(countCell);
@ -632,8 +628,11 @@
setRack(data.rack);
}
if (!data.action) {
return;
}
switch(data.action) {
switch (data.action) {
case "pushBag": //TODO
break;
@ -656,13 +655,13 @@
}
if (data.to === "board") {
setCell(data.indexTo, data.letter, data.hasOwnProperty("player") && data.player !== localStorage.trivabblePlayerName);
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
} else if (data.to === "rack") {
setRackCell(data.indexTo, data.letter);
}
break;
case "setCell":
setCell(data.indexTo, data.letter, data.hasOwnProperty("player") && data.player !== localStorage.trivabblePlayerName);
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
break;
case "setRackCell":
setRackCell(data.indexTo, data.letter);
@ -849,7 +848,7 @@
function (newName) {
if (newName && newName.trim()) {
localStorage.trivabblePlayerName = newName.trim();
document.getElementById("name").textContent = localStorage.trivabblePlayerName;
name.textContent = localStorage.trivabblePlayerName;
}
},
localStorage.trivabblePlayerName
@ -975,7 +974,7 @@
audioSourceOGG = document.createElement("source");
audioSourceOGG.src = "receive.ogg";
var audioSourceMP3 = document.createElement("source");
audioSourceMP3 = document.createElement("source");
audioSourceMP3.src = "receive.mp3";
audioChat.appendChild(audioSourceOGG);
audioChat.appendChild(audioSourceMP3);
@ -992,13 +991,13 @@
localStorage.trivabbleMsgSound = msgSound.checked;
};
if (localStorage.hasOwnProperty("trivabbleMsgSound")) {
if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleMsgSound")) {
msgSound.checked = localStorage.trivabbleMsgSound === "true";
} else {
localStorage.trivabbleMsgSound = msgSound.checked;
}
if (localStorage.hasOwnProperty("trivabbleTileSound")) {
if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleTileSound")) {
tilesSound.checked = localStorage.trivabbleTileSound === "true";
} else {
localStorage.trivabbleTilesSound = tilesSound.checked;
@ -1078,7 +1077,7 @@
return;
}
document.getElementById("name").textContent = localStorage.trivabblePlayerName;
name.textContent = localStorage.trivabblePlayerName;
var letters = "ABCDEFGHIJKLMNO";
@ -1121,7 +1120,7 @@
if (i === j && i === 7) {
specialCell("doubleWord", board.lastChild.lastChild);
var cell = board.lastChild.lastChild.getElementsByClassName("special-cell-label")[0];
cell = board.lastChild.lastChild.getElementsByClassName("special-cell-label")[0];
cell.textContent = "★";
board.lastChild.lastChild.id = "center-cell";
} else if (i % 7 === 0 && j % 7 === 0) {