diff --git a/client/robots.txt b/client/robots.txt
new file mode 100644
index 0000000..d346249
--- /dev/null
+++ b/client/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /api/
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js
index 1679ae5..2dd9b54 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/GameCreationStepManager.js
@@ -319,6 +319,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null)
backButton.innerText = "\u2bc7 Back";
backButton.addEventListener('click', backHandler);
backButton.setAttribute("id", "step-back-button");
+ backButton.classList.add('cancel');
document.getElementById("tracker-container").prepend(backButton);
}
diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js
index 7ef70cb..c1179a5 100644
--- a/client/src/modules/Templates.js
+++ b/client/src/modules/Templates.js
@@ -116,7 +116,7 @@ export const templates = {
"
Transfer Mod Powers 👑
" +
"" +
"" +
- "" +
+ "" +
"
" +
"" +
"" +
diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js
index ef6086f..5e77c73 100644
--- a/client/src/scripts/game.js
+++ b/client/src/scripts/game.js
@@ -141,6 +141,7 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer,
}
break;
case globals.STATUS.ENDED:
+ document.querySelector("#end-game-prompt")?.remove();
let container = document.getElementById("game-state-container")
container.innerHTML = templates.END_OF_GAME_VIEW;
container.classList.add('vertical-flex');
diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css
index 0efac8e..7afcd54 100644
--- a/client/src/styles/GLOBAL.css
+++ b/client/src/styles/GLOBAL.css
@@ -23,7 +23,8 @@ th, thead, tr, tt, u, ul, var {
html {
font-family: 'signika-negative', sans-serif !important;
- background-color: #121314 !important;
+ background-color: #0f0f10;
+ height: 100%;
}
body {
@@ -48,7 +49,7 @@ body {
h1 {
font-family: 'diavlo', sans-serif;
- color: #ab2626;
+ color: #b1afcd;
filter: drop-shadow(2px 2px 4px black);
margin: 0.5em 0;
}
@@ -154,6 +155,11 @@ button:active, input[type=submit]:active {
border: 2px solid #21ba45;
}
+.cancel:hover {
+ background-color: #623232 !important;
+ border: 2px solid #8a1c1c;
+}
+
.container {
padding: 5px;
border-radius: 3px;
@@ -206,6 +212,7 @@ input {
padding: 5px 0;
width: 100%;
background-color: #333243;
+ border-bottom: 2px solid #57566a
}
#navbar img {
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index 137881e..83626d3 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -85,9 +85,10 @@
#deck-container, #custom-roles-container {
margin: 1em 0;
- background-color: #1f1f1f;
+ background-color: #191920;
padding: 10px;
border-radius: 3px;
+ border: 2px solid #333243;
}
#step-3 {
@@ -101,7 +102,7 @@
}
option {
- background-color: #1f1f1f;
+ background-color: #191920;
cursor: pointer;
}
@@ -148,7 +149,7 @@ select {
}
#game-form > div {
- background-color: #1f1f1f;
+ background-color: #191920;
display: flex;
flex-direction: column;
padding: 10px;
@@ -164,7 +165,8 @@ select {
#game-time {
display: flex;
flex-wrap: wrap;
- background-color: #1f1f1f;
+ background-color: #191920;
+ border: 2px solid #333243;
border-radius: 3px;
}
@@ -290,13 +292,13 @@ input[type="number"] {
}
#step-1 div {
- background-color: black;
+ background-color: #191920;
color: whitesmoke;
padding: 1em;
max-width: 20em;
margin: 0.5em;
cursor: pointer;
- border: 2px solid transparent;
+ border: 2px solid #333243;
border-radius: 3px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6);
}
@@ -315,7 +317,8 @@ input[type="number"] {
}
.review-option {
- background-color: #1f1f1f;
+ background-color: #191920;
+ border: 2px solid #333243;
color: whitesmoke;
padding: 10px;
font-size: 18px;
diff --git a/client/src/styles/game.css b/client/src/styles/game.css
index c92930d..5577605 100644
--- a/client/src/styles/game.css
+++ b/client/src/styles/game.css
@@ -178,6 +178,7 @@ h1 {
#role-info-modal #modal-button-container {
margin-top: 1em;
+ justify-content: center;
}
#game-role-info-container .role-info-name {
@@ -186,7 +187,7 @@ h1 {
font-size: 20px;
font-family: signika-negative, sans-serif;
margin: 0.5em 0;
- background-color: #15191c;
+ background-color: black;
}
#role-info-modal h2 {
@@ -541,7 +542,15 @@ label[for='moderator'] {
}
.reveal-role-button {
- background-color: #3f5256;
+ background-color: #586a6e;
+}
+
+.reveal-role-button:hover, #mod-transfer-button:hover {
+ background-color: #4e5664;
+}
+
+.kill-player-button:hover {
+ background-color: #b35c5c;
}
.reveal-role-button img {
diff --git a/client/src/styles/home.css b/client/src/styles/home.css
index ddf547d..a2b3e5f 100644
--- a/client/src/styles/home.css
+++ b/client/src/styles/home.css
@@ -1,5 +1,6 @@
html {
- height: 100%;
+ background: rgb(0,0,0);
+ background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgb(17 18 18) 35%, rgba(27,31,31,1) 100%);
}
body {
@@ -18,7 +19,7 @@ form {
margin: 1em 0;
padding: 10px;
border-radius: 3px;
- background-color: #1f1f1f;
+ background-color: black;
justify-content: center;
align-items: center;
}
@@ -70,7 +71,7 @@ form > div {
#join-container > label {
font-size: 35px;
font-family: 'diavlo', sans-serif;
- color: #ab2626;
+ color: #b1afcd;
filter: drop-shadow(2px 2px 4px black);
}
diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css
index 28f7bd3..20b41e8 100644
--- a/client/src/styles/modal.css
+++ b/client/src/styles/modal.css
@@ -7,7 +7,7 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- background-color: #23282b;
+ background-color: #191920;
align-items: center;
justify-content: center;
max-width: 25em;
diff --git a/client/src/views/create.html b/client/src/views/create.html
index 5f70e52..0db4447 100644
--- a/client/src/views/create.html
+++ b/client/src/views/create.html
@@ -42,7 +42,7 @@
-
+
diff --git a/manifest.json b/manifest.json
index 13566cd..0e32c7b 100644
--- a/manifest.json
+++ b/manifest.json
@@ -18,6 +18,5 @@
"start_url": "/?source=pwa",
"display": "fullscreen",
"display_override": ["fullscreen", "standalone", "minimal-ui"],
-
"description": "A utility to deal Werewolf cards and run games in any setting, on any device."
}
diff --git a/package.json b/package.json
index 76b9fad..c37d3f7 100644
--- a/package.json
+++ b/package.json
@@ -7,8 +7,8 @@
"bundle": "webpack --config client/webpack/webpack-prod.config.js",
"prestart": "npm run bundle",
"build:dev": "webpack --watch --config client/webpack/webpack-dev.config.js --mode=development",
- "start:dev": "NODE_ENV=development node server/main.js",
- "start:dev:windows": "SET NODE_ENV=development && node server/main.js",
+ "start:dev": "NODE_ENV=development nodemon server/main.js",
+ "start:dev:windows": "SET NODE_ENV=development && nodemon server/main.js",
"start": "NODE_ENV=production node server/main.js -- loglevel=warn port=8080",
"start:windows": "SET NODE_ENV=production && node server/main.js -- loglevel=warn port=8080",
"test": "jasmine && node browsertest.js openBrowser socket",
@@ -26,6 +26,7 @@
"@babel/preset-env": "^7.16.5",
"acorn": "^8.6.0",
"babel-loader": "^8.2.3",
+ "body-parser": "^1.19.1",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-force-https": "^1.0.0",
@@ -38,5 +39,8 @@
"webpack-cli": "^4.9.1",
"webpack-remove-debug": "^0.1.0"
},
- "devDependencies": {}
+ "devDependencies": {},
+ "nodemonConfig": {
+ "ignore": ["client/*", "node_modules/*"]
+ }
}
diff --git a/server/main.js b/server/main.js
index fedfe26..4d4a4ab 100644
--- a/server/main.js
+++ b/server/main.js
@@ -1,114 +1,33 @@
const express = require('express');
-const http = require('http');
-const https = require('https');
const path = require('path');
-const fs = require('fs');
const app = express();
-const cors = require('cors')
const bodyParser = require('body-parser');
const GameManager = require('./modules/GameManager.js');
const globals = require('./config/globals');
-// const QueueManager = require('./modules/managers/QueueManager');
+const ServerBootstrapper = require('./modules/ServerBootstrapper');
-app.use(bodyParser.json()); // to support JSON-encoded bodies
-app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({
extended: true
}));
-let main, environment;
+const args = ServerBootstrapper.processCLIArgs();
-let args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase()));
+const logger = require('./modules/Logger')(args.logLevel);
+logger.log('LOG LEVEL IS: ' + args.logLevel);
-const localServer = args.includes('local');
-const useHttps = args.includes('https');
-const port = process.env.PORT || args
- .filter((arg) => {
- return /port=\d+/.test(arg);
- })
- .map((arg) => {
- return /port=(\d+)/.exec(arg)[1];
- })[0] || 5000;
-const logLevel = process.env.LOG_LEVEL || args
- .filter((arg) => {
- return /loglevel=[a-zA-Z]+/.test(arg);
- })
- .map((arg) => {
- return /loglevel=([a-zA-Z]+)/.exec(arg)[1];
- })[0] || globals.LOG_LEVEL.INFO;
+const main = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger)
-const logger = require('./modules/Logger')(logLevel);
+app.set('port', args.port);
-logger.log('LOG LEVEL IS: ' + logLevel)
-
-if (localServer) {
- environment = globals.ENVIRONMENT.LOCAL;
- logger.log('starting main in LOCAL mode.');
- if (useHttps && fs.existsSync(path.join(__dirname, '../client/certs/localhost-key.pem')) && fs.existsSync(path.join(__dirname, '../client/certs/localhost.pem'))) {
- const key = fs.readFileSync(path.join(__dirname, '../client/certs/localhost-key.pem'), 'utf-8');
- const cert = fs.readFileSync(path.join(__dirname, '../client/certs/localhost.pem'), 'utf-8');
- logger.log('local certs detected. Using HTTPS.');
- main = https.createServer({ key, cert }, app);
- logger.log(`navigate to https://localhost:${port}`);
- } else {
- logger.log('https not specified or no local certs detected. Using HTTP.');
- main = http.createServer(app);
- logger.log(`navigate to http://localhost:${port}`);
- }
-} else {
- environment = globals.ENVIRONMENT.PRODUCTION;
- logger.warn('starting main in PRODUCTION mode. This should not be used for local development.');
- main = http.createServer(app);
- const secure = require('express-force-https');
- app.use(secure);
-}
-
-app.set('port', port);
-
-let io;
-
-if (process.env.NODE_ENV.trim() === 'development') {
- const corsOptions = {
- origin: "http://localhost:" + port,
- optionsSuccessStatus: 200,
- methods: ["GET", "POST"]
- }
- app.use(cors(corsOptions));
- io = require("socket.io")(main, {
- cors: {
- origin: "http://localhost:" + port,
- methods: ["GET", "POST"],
- allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
- credentials: false
- }
- });
-} else {
- const corsOptions = {
- origin: ["https://playwerewolf.uk.r.appspot.com"],
- methods: ["GET", "POST"],
- allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
- optionsSuccessStatus: 200,
- }
- app.use(cors(corsOptions));
- io = require("socket.io")(main, {
- cors: {
- origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"],
- methods: ["GET", "POST"],
- allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
- credentials: false
- },
- transports: ["polling"]
- });
-}
-
-const inGame = io.of('/in-game');
+const inGameSocketServer = ServerBootstrapper.createSocketServer(main, app, args.port);
+inGameSocketServer.on('connection', function (socket) {
+ gameManager.addGameSocketHandlers(inGameSocketServer, socket);
+});
/* Instantiate the singleton game manager */
-//const gameManager = new GameManager(logger, environment).getInstance();
-const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); // temporary
-
-/* Instantiate the singleton queue manager */
-//const queueManager = new QueueManager(matchmaking, logger).getInstance();
+const gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL).getInstance(); // temporarily use local environment configuration for game manager
/* api endpoints */
const games = require('./api/GamesAPI');
@@ -126,26 +45,20 @@ app.use('/favicon.ico', (req, res) => {
const router = require('./routes/router');
app.use('', router);
-if (process.env.NODE_ENV.trim() === 'development') {
- app.use('/dist', express.static(path.join(__dirname, '../client/dist')));
-} else if (process.env.NODE_ENV.trim() === 'production') {
- app.use('/dist', express.static(path.join(__dirname, '../client/dist')));
-}
+app.use('/dist', express.static(path.join(__dirname, '../client/dist')));
// set up routing for static content that isn't being bundled.
app.use('/images', express.static(path.join(__dirname, '../client/src/images')));
app.use('/styles', express.static(path.join(__dirname, '../client/src/styles')));
app.use('/webfonts', express.static(path.join(__dirname, '../client/src/webfonts')));
-
+app.use('/robots.txt', (req, res) => {
+ res.sendFile(path.join(__dirname, '../client/robots.txt'));
+});
app.use(function (req, res) {
res.sendFile(path.join(__dirname, '../client/src/views/404.html'));
});
-inGame.on('connection', function (socket) {
- gameManager.addGameSocketHandlers(inGame, socket);
-});
-
-main.listen(port, function () {
- logger.log(`Starting server on port ${port}` );
+main.listen(args.port, function () {
+ logger.log(`Starting server on port ${args.port}` );
});
diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js
new file mode 100644
index 0000000..345fe3b
--- /dev/null
+++ b/server/modules/ServerBootstrapper.js
@@ -0,0 +1,106 @@
+const LOG_LEVEL = require('../config/globals').LOG_LEVEL;
+const http = require('http');
+const https = require('https');
+const path = require('path');
+const fs = require('fs');
+const cors = require('cors')
+
+const ServerBootstrapper = {
+ processCLIArgs: () => {
+ try {
+ let args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase()));
+ const useHttps = args.includes('https');
+ const port = process.env.PORT || args
+ .filter((arg) => {
+ return /port=\d+/.test(arg);
+ })
+ .map((arg) => {
+ return /port=(\d+)/.exec(arg)[1];
+ })[0] || 5000;
+ const logLevel = process.env.LOG_LEVEL || args
+ .filter((arg) => {
+ return /loglevel=[a-zA-Z]+/.test(arg);
+ })
+ .map((arg) => {
+ return /loglevel=([a-zA-Z]+)/.exec(arg)[1];
+ })[0] || LOG_LEVEL.INFO;
+
+ return {
+ useHttps: useHttps,
+ port: port,
+ logLevel: logLevel
+ };
+ } catch (e) {
+ throw new Error("Your server run command is malformed. Consult the codebase wiki for proper usage. Error: " + e);
+ }
+ },
+
+ createServerWithCorrectHTTPProtocol: (app, useHttps, port, logger) => {
+ let main;
+ if (process.env.NODE_ENV.trim() === 'development') {
+ logger.log('starting main in DEVELOPMENT mode.');
+ if (
+ useHttps
+ && fs.existsSync(path.join(__dirname, '../../client/certs/localhost-key.pem'))
+ && fs.existsSync(path.join(__dirname, '../../client/certs/localhost.pem'))
+ ) {
+ const key = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost-key.pem'), 'utf-8');
+ const cert = fs.readFileSync(path.join(__dirname, '../../client/certs/localhost.pem'), 'utf-8');
+ logger.log('local certs detected. Using HTTPS.');
+ main = https.createServer({ key, cert }, app);
+ logger.log(`navigate to https://localhost:${port}`);
+ } else {
+ logger.log('https not specified or no local certs detected. Certs should reside in /client/certs. Using HTTP.');
+ main = http.createServer(app);
+ logger.log(`navigate to http://localhost:${port}`);
+ }
+ } else {
+ logger.warn('starting main in PRODUCTION mode. This should not be used for local development.');
+ main = http.createServer(app);
+ app.use(require('express-force-https'));
+ }
+
+ return main;
+ },
+
+ createSocketServer: (main, app, port) => {
+ let io;
+ if (process.env.NODE_ENV.trim() === 'development') {
+ const corsOptions = {
+ origin: "http://localhost:" + port,
+ optionsSuccessStatus: 200,
+ methods: ["GET", "POST"]
+ }
+ app.use(cors(corsOptions));
+ io = require("socket.io")(main, {
+ cors: {
+ origin: "http://localhost:" + port,
+ methods: ["GET", "POST"],
+ allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
+ credentials: false
+ }
+ });
+ } else {
+ const corsOptions = {
+ origin: ["https://playwerewolf.uk.r.appspot.com"],
+ methods: ["GET", "POST"],
+ allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
+ optionsSuccessStatus: 200,
+ }
+ app.use(cors(corsOptions));
+ io = require("socket.io")(main, {
+ cors: {
+ origin: ["https://playwerewolf.uk.r.appspot.com", "wss://playwerewolf.uk.r.appspot.com"],
+ methods: ["GET", "POST"],
+ allowedHeaders: ["Content-Type", "X-Requested-With", "Accept"],
+ credentials: false
+ },
+ transports: ["polling"]
+ });
+ }
+
+ return io.of('/in-game');
+ }
+}
+
+module.exports = ServerBootstrapper;