diff --git a/app.yaml b/app.yaml
index 98f616c..b55ef99 100644
--- a/app.yaml
+++ b/app.yaml
@@ -1,5 +1,19 @@
runtime: nodejs
env: flex
-entrypoint: npm run start:prod:linux
-automatic_scaling:
- max_instances: 1
+network:
+ session_affinity: true
+liveness_check:
+ path: "/liveness_check"
+ check_interval_sec: 60
+ timeout_sec: 4
+ failure_threshold: 2
+ success_threshold: 2
+readiness_check:
+ path: "/readiness_check"
+ check_interval_sec: 60
+ timeout_sec: 4
+ failure_threshold: 2
+ success_threshold: 2
+ app_start_timeout_sec: 600
+manual_scaling:
+ instances: 1
diff --git a/client/src/images/GitHub-Mark-Light-120px-plus.png b/client/src/images/GitHub-Mark-Light-120px-plus.png
new file mode 100644
index 0000000..192846a
Binary files /dev/null and b/client/src/images/GitHub-Mark-Light-120px-plus.png differ
diff --git a/client/src/images/GitHub-Mark-Light-32px.png b/client/src/images/GitHub-Mark-Light-32px.png
deleted file mode 100644
index 628da97..0000000
Binary files a/client/src/images/GitHub-Mark-Light-32px.png and /dev/null differ
diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js
index 35ab21b..5b5415d 100644
--- a/client/src/modules/DeckStateManager.js
+++ b/client/src/modules/DeckStateManager.js
@@ -1,7 +1,7 @@
export class DeckStateManager {
constructor() {
this.deck = null;
- this.customRoleOptions = null;
+ this.customRoleOptions = [];
}
addToDeck(role) {
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js
index 96345b0..a47a1a4 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/GameCreationStepManager.js
@@ -67,7 +67,6 @@ export class GameCreationStepManager {
this.currentGame.hasTimer = false;
this.currentGame.timerParams = null;
}
- console.log(this.currentGame);
cancelCurrentToast();
this.removeStepElementsFromDOM(this.step);
this.incrementStep();
@@ -102,6 +101,10 @@ export class GameCreationStepManager {
) {
window.location = ('/game/' + res.content);
}
+ }).catch((e) => {
+ if (e.status === 429) {
+ toast('You\'ve sent this request too many times.', 'error', true, true, 6);
+ }
});
}
}
@@ -450,8 +453,9 @@ function initializeRemainingEventListeners(deckManager) {
e.preventDefault();
let name = document.getElementById("role-name").value.trim();
let description = document.getElementById("role-description").value.trim();
+ let team = document.getElementById("role-alignment").value.toLowerCase().trim();
if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name
- deckManager.addToCustomRoleOptions({role: name, description: description});
+ deckManager.addToCustomRoleOptions({role: name, description: description, team: team});
updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select"))
ModalManager.dispelModal("add-role-modal", "add-role-modal-background");
toast("Role Added", "success", true);
@@ -481,10 +485,10 @@ function addOptionsToList(options, selectEl) {
});
for (let i = 0; i < options.length; i ++) {
let optionEl = document.createElement("option");
- let alignmentClass = customCards[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL
+ let alignmentClass = options[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL
optionEl.classList.add(alignmentClass);
- optionEl.setAttribute("value", customCards[i].role);
- optionEl.innerText = customCards[i].role;
+ optionEl.setAttribute("value", options[i].role);
+ optionEl.innerText = options[i].role;
selectEl.appendChild(optionEl);
}
}
diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js
index 5b59379..aabf69e 100644
--- a/client/src/modules/GameStateRenderer.js
+++ b/client/src/modules/GameStateRenderer.js
@@ -39,7 +39,9 @@ export class GameStateRenderer {
title.innerText = "Lobby";
document.getElementById("game-title").appendChild(title);
let gameLinkContainer = document.getElementById("game-link");
- gameLinkContainer.innerText = window.location;
+ let linkDiv = document.createElement("div");
+ linkDiv.innerText = window.location;
+ gameLinkContainer.prepend(linkDiv);
gameLinkContainer.addEventListener('click', () => {
navigator.clipboard.writeText(gameLinkContainer.innerText).then(() => {
toast('Link copied!', 'success', true);
@@ -265,7 +267,6 @@ function renderPotentialMods(gameState, group, transferModHandlers, socket) {
container.addEventListener('click', transferModHandlers[member.id]);
modalContent.appendChild(container);
- console.log('test');
}
}
}
diff --git a/client/src/modules/GameTimerManager.js b/client/src/modules/GameTimerManager.js
index ad34eba..4fe03f9 100644
--- a/client/src/modules/GameTimerManager.js
+++ b/client/src/modules/GameTimerManager.js
@@ -114,7 +114,6 @@ export class GameTimerManager {
if(!socket.hasListeners(globals.COMMANDS.GET_TIME_REMAINING)) {
socket.on(globals.COMMANDS.GET_TIME_REMAINING, (timeRemaining, paused) => {
- console.log('received time remaining from server');
if (paused) {
this.displayPausedTime(timeRemaining);
} else if (timeRemaining === 0) {
@@ -151,6 +150,16 @@ export class GameTimerManager {
pauseBtn.addEventListener('click', this.pauseListener);
document.getElementById('play-pause').appendChild(pauseBtn);
}
+
+ processTimeRemaining(timeRemaining, paused, timerWorker) {
+ if (paused) {
+ this.displayPausedTime(timeRemaining);
+ } else if (timeRemaining === 0) {
+ this.displayExpiredTime();
+ } else {
+ this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker);
+ }
+ }
}
function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) {
diff --git a/client/src/modules/StateBucket.js b/client/src/modules/StateBucket.js
index 62c624a..7571fc2 100644
--- a/client/src/modules/StateBucket.js
+++ b/client/src/modules/StateBucket.js
@@ -1,5 +1,6 @@
/* It started getting confusing where I am reading/writing to the game state, and thus the state started to get inconsistent.
Creating a bucket to hold it so I can overwrite the gameState object whilst still preserving a reference to the containing bucket.
+ Now several components can read a shared game state.
*/
export const stateBucket = {
currentGameState: null,
diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js
index ae6a5b3..7ef70cb 100644
--- a/client/src/modules/Templates.js
+++ b/client/src/modules/Templates.js
@@ -128,6 +128,9 @@ export const templates = {
"" +
"
" + "
" +
"" +
+ "" +
+ "
" +
"" +
"" +
"
" +
diff --git a/client/src/scripts/create.js b/client/src/scripts/create.js
index 5b1ae6d..dd9dcaa 100644
--- a/client/src/scripts/create.js
+++ b/client/src/scripts/create.js
@@ -7,7 +7,7 @@ const create = () => {
let deckManager = new DeckStateManager();
let gameCreationStepManager = new GameCreationStepManager(deckManager);
loadDefaultCards(deckManager);
- loadCustomRoles(deckManager);
+ //loadCustomRoles(deckManager);
gameCreationStepManager.renderStep("creation-step-container", 1);
}
diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js
index cd3fdaa..32307c7 100644
--- a/client/src/scripts/game.js
+++ b/client/src/scripts/game.js
@@ -41,7 +41,6 @@ function prepareGamePage(environment, socket, timerWorker) {
document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM;
toast('You are connected.', 'success', true, true, 2);
- console.log(gameState);
userId = gameState.client.cookie;
UserUtility.setAnonymousUserId(userId, environment);
let gameStateRenderer = new GameStateRenderer(stateBucket, socket);
@@ -66,7 +65,7 @@ function prepareGamePage(environment, socket, timerWorker) {
ModalManager.dispelModal("change-name-modal", "change-name-modal-background")
toast('Name set.', 'success', true, true, 5);
propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id);
- processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer);
+ processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker);
}
})
} else {
@@ -82,10 +81,10 @@ function prepareGamePage(environment, socket, timerWorker) {
function initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager) {
setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager);
- processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer);
+ processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker);
}
-function processGameState (currentGameState, userId, socket, gameStateRenderer) {
+function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker) {
displayClientInfo(currentGameState.client.name, currentGameState.client.userType);
switch (currentGameState.status) {
case globals.STATUS.LOBBY:
@@ -133,7 +132,9 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer)
break;
}
if (currentGameState.timerParams) {
- socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode);
+ socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode, (timeRemaining, paused) => {
+ gameTimerManager.processTimeRemaining(timeRemaining, paused, timerWorker);
+ });
} else {
document.querySelector('#game-timer')?.remove();
document.querySelector('label[for="game-timer"]')?.remove();
@@ -183,7 +184,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
stateBucket.currentGameState.client.cookie,
function (gameState) {
stateBucket.currentGameState = gameState;
- processGameState(stateBucket.currentGameState, gameState.client.cookie, socket, gameStateRenderer);
+ processGameState(stateBucket.currentGameState, gameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker);
}
);
});
@@ -252,7 +253,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
socket.on(globals.EVENTS.CHANGE_NAME, (personId, name) => {
propagateNameChange(stateBucket.currentGameState, name, personId);
updateDOMWithNameChange(stateBucket.currentGameState, gameStateRenderer);
- processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer);
+ processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker);
});
}
@@ -260,7 +261,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
socket.on(globals.COMMANDS.END_GAME, (people) => {
stateBucket.currentGameState.people = people;
stateBucket.currentGameState.status = globals.STATUS.ENDED;
- processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer);
+ processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer, gameTimerManager, timerWorker);
});
}
}
diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css
index 8b394eb..0efac8e 100644
--- a/client/src/styles/GLOBAL.css
+++ b/client/src/styles/GLOBAL.css
@@ -31,6 +31,19 @@ body {
flex-direction: column;
align-items: flex-start;
margin: 0 auto;
+ position: relative;
+}
+
+.bmc-btn {
+ height: 40px !important;
+ border-radius: 3px !important;
+ font-size: 18px !important;
+ min-width: 180px !important;
+ padding: 0 17px !important;
+}
+
+.bmc-btn-text {
+ margin: 0 !important;
}
h1 {
@@ -76,6 +89,7 @@ h3 {
text-decoration: none;
cursor: pointer;
font-family: 'diavlo', sans-serif;
+ margin: 0 0.25em;
}
#footer a:hover {
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index 67bb855..1c925d5 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -257,6 +257,7 @@ input[type="number"] {
#game-creation-container {
width: 95%;
max-width: 60em;
+ position: relative;
}
#tracker-container {
diff --git a/client/src/styles/game.css b/client/src/styles/game.css
index 5e227f1..c92930d 100644
--- a/client/src/styles/game.css
+++ b/client/src/styles/game.css
@@ -58,6 +58,7 @@ body {
#lobby-header {
margin-bottom: 1em;
+ max-width: 95%;
}
h1 {
@@ -115,13 +116,6 @@ h1 {
}
#game-link {
- user-select: none;
- -ms-user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
cursor: pointer;
margin-top: 10px;
padding: 7px;
@@ -134,6 +128,13 @@ h1 {
transition: background-color 0.2s;
}
+#game-link > div {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ width: 95%;
+}
+
.role-info-name {
display: flex;
}
@@ -277,6 +278,9 @@ h1 {
font-size: 35px;
text-shadow: 0 3px 4px rgb(0 0 0 / 85%);
border: 1px solid #747474;
+ min-width: 5em;
+ display: flex;
+ justify-content: center;
}
#game-timer.low {
diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css
index 7e4b579..28f7bd3 100644
--- a/client/src/styles/modal.css
+++ b/client/src/styles/modal.css
@@ -1,7 +1,7 @@
.modal {
border-radius: 2px;
text-align: center;
- position: fixed;
+ position: absolute;
width: 85%;
z-index: 100;
top: 50%;
@@ -11,7 +11,6 @@
align-items: center;
justify-content: center;
max-width: 25em;
- height: fit-content;
font-family: sans-serif;
flex-direction: column;
padding: 1em;
@@ -49,3 +48,11 @@
justify-content: space-between;
flex-direction: row;
}
+
+#change-name-modal, #transfer-mod-modal, #role-info-modal {
+ position: fixed;
+}
+
+#role-info-modal, #transfer-mod-modal {
+ max-height: 80%;
+}
diff --git a/client/src/views/game.html b/client/src/views/game.html
index 29a9f7d..f5e08b6 100644
--- a/client/src/views/game.html
+++ b/client/src/views/game.html
@@ -62,8 +62,8 @@