summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2021-11-17 13:39:16 +0100
committerTor Andersson <tor@ccxvii.net>2021-11-17 19:05:15 +0100
commit327acfe1124cdafc5eb460a039222a160f867ba3 (patch)
tree20dc04fc3ed170881af2caf9ba0ae58a62f6020e
parent7b5ce7d3949755c12b663531ab5d0b3d3051de34 (diff)
downloadserver-327acfe1124cdafc5eb460a039222a160f867ba3.tar.gz
Simplify URL for playing games.
-rw-r--r--connect-better-sqlite3.js2
-rw-r--r--public/common/client.js38
-rw-r--r--public/join.js11
-rw-r--r--server.js86
-rw-r--r--views/games.ejs44
-rw-r--r--views/info.ejs74
-rw-r--r--views/join.ejs16
-rw-r--r--views/profile.ejs55
8 files changed, 193 insertions, 133 deletions
diff --git a/connect-better-sqlite3.js b/connect-better-sqlite3.js
index 008114b..6574346 100644
--- a/connect-better-sqlite3.js
+++ b/connect-better-sqlite3.js
@@ -31,7 +31,7 @@ module.exports = function (session) {
let db = new SQLite(db_path, options.mode);
db.pragma("journal_mode = WAL");
db.pragma("synchronous = OFF");
- db.exec("CREATE TABLE IF NOT EXISTS "+table+" (sid PRIMARY KEY, expires INTEGER, sess TEXT)");
+ db.exec("CREATE TABLE IF NOT EXISTS "+table+" (sid PRIMARY KEY, expires INTEGER, sess TEXT) WITHOUT ROWID");
db.exec("DELETE FROM "+table+" WHERE "+now()+" > expires");
db.exec("VACUUM");
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
diff --git a/public/common/client.js b/public/common/client.js
index 1e31f79..ccc9718 100644
--- a/public/common/client.js
+++ b/public/common/client.js
@@ -2,6 +2,16 @@
/* global io, on_update */
+/* URL: /$title_id/play:$game_id:$role */
+if (!/\/[\w-]+\/play:\d+(:[\w-]+)?/.test(window.location.pathname)) {
+ document.querySelector("#prompt").textContent = "Invalid game ID.";
+ throw Error("Invalid game ID.");
+}
+
+const param_title_id = window.location.pathname.split("/")[1];
+const param_game_id = window.location.pathname.split("/")[2].split(":")[1] | 0;
+const param_role = window.location.pathname.split("/")[2].split(":")[2] || "Observer";
+
let game = null;
let game_over = false;
let player = null;
@@ -105,8 +115,8 @@ function stop_blinker() {
window.addEventListener("focus", stop_blinker);
-function load_chat(game_id) {
- chat_key = "chat/" + game_id;
+function load_chat() {
+ chat_key = "chat/" + param_game_id;
chat_text = document.querySelector(".chat_text");
chat_last_day = null;
chat_log = 0;
@@ -158,11 +168,6 @@ function update_chat(chat_id, utc_date, user, message) {
}
function init_client(roles) {
- let params = new URLSearchParams(window.location.search);
- let title = window.location.pathname.split("/")[1];
- let game_id = params.get("game");
- let role = params.get("role");
-
game = null;
player = null;
@@ -186,13 +191,13 @@ function init_client(roles) {
".role.seven .role_user",
];
- load_chat(game_id);
+ load_chat();
- console.log("JOINING", title + "/" + game_id + "/" + role);
+ console.log("JOINING", param_title_id + "/" + param_game_id + "/" + param_role);
socket = io({
transports: ['websocket'],
- query: { title: title, game: game_id, role: role },
+ query: { title: param_title_id, game: param_game_id, role: param_role },
});
socket.on('connect', () => {
@@ -242,7 +247,7 @@ function init_client(roles) {
socket.on('save', (msg) => {
console.log("SAVE");
- window.localStorage[title + '/save'] = msg;
+ window.localStorage[param_title_id + '/save'] = msg;
});
socket.on('error', (msg) => {
@@ -446,8 +451,7 @@ function send_save() {
}
function send_restore() {
- let title = window.location.pathname.split("/")[1];
- socket.emit('restore', window.localStorage[title + '/save']);
+ socket.emit('restore', window.localStorage[param_title_id + '/save']);
}
function send_restart(scenario) {
@@ -474,13 +478,9 @@ function on_game_over() {
}
function send_rematch() {
- let params = new URLSearchParams(window.location.search);
- let game_id = params.get("game");
- let role_id = params.get("role");
- window.location = '/rematch/' + game_id + '/' + role_id;
+ window.location = '/rematch/' + param_game_id + '/' + param_role;
}
function send_exit() {
- let title = window.location.pathname.split("/")[1];
- window.location = '/info/' + title;
+ window.location = '/info/' + param_title_id;
}
diff --git a/public/join.js b/public/join.js
index ce3d0d0..898113b 100644
--- a/public/join.js
+++ b/public/join.js
@@ -60,7 +60,7 @@ function start_event_source() {
evtsrc.addEventListener("game", function (evt) {
console.log("GAME:", evt.data);
game = JSON.parse(evt.data);
- if (game.status > 1) {
+ if (game.status > 0) {
clearInterval(timer);
evtsrc.close();
}
@@ -85,9 +85,6 @@ function is_solo() {
}
function update() {
- window.game_status.textContent = ["Open","Active","Finished","Abandoned"][game.status];
- window.game_result.textContent = game.result || "\u2014";
-
for (let i = 0; i < roles.length; ++i) {
let role = roles[i];
let role_id = "role_" + role.replace(/ /g, "_");
@@ -103,7 +100,7 @@ function update() {
else
element.className = "";
if (player.user_id === user_id)
- element.innerHTML = `<a href="/play/${game.game_id}/${role}">Play</a>`;
+ element.innerHTML = `<a href="/${game.title_id}/play:${game.game_id}:${role}">Play</a>`;
else
element.innerHTML = player.name;
} else {
@@ -129,7 +126,7 @@ function update() {
else
message.innerHTML = "Waiting for players to join...";
} else {
- message.innerHTML = `<a class="play" href="/play/${game.game_id}/Observer">Observe</a>`;
+ message.innerHTML = `<a href="/${game.title_id}/play:${game.game_id}">Observe</a>`;
}
if (game.owner_id === user_id) {
@@ -150,7 +147,7 @@ function update() {
window.onload = function () {
update();
- if (game.status < 2) {
+ if (game.status === 0) {
start_event_source();
timer = setInterval(start_event_source, 15000);
}
diff --git a/server.js b/server.js
index 02411db..c017b0c 100644
--- a/server.js
+++ b/server.js
@@ -907,7 +907,9 @@ const SQL_SELECT_PLAYERS_JOIN = SQL("SELECT role, user_id, name FROM players NAT
const SQL_SELECT_PLAYER_ROLE = SQL("SELECT role FROM players WHERE game_id=? AND user_id=?").pluck();
const SQL_INSERT_PLAYER_ROLE = SQL("INSERT OR IGNORE INTO players (game_id,role,user_id) VALUES (?,?,?)");
const SQL_DELETE_PLAYER_ROLE = SQL("DELETE FROM players WHERE game_id=? AND role=?");
-const SQL_UPDATE_PLAYER_ROLE = db.prepare("UPDATE players SET role=? WHERE game_id=? AND role=? AND user_id=?");
+const SQL_UPDATE_PLAYER_ROLE = SQL("UPDATE players SET role=? WHERE game_id=? AND role=? AND user_id=?");
+
+const SQL_AUTHORIZE_GAME_ROLE = SQL("SELECT 1 FROM players NATURAL JOIN games WHERE title_id=? AND game_id=? AND role=? AND user_id=?").pluck();
const SQL_SELECT_OPEN_GAMES = db.prepare("SELECT * FROM games WHERE status=0");
const SQL_COUNT_OPEN_GAMES = SQL("SELECT COUNT(*) FROM games WHERE owner_id=? AND status=0").pluck();
@@ -927,12 +929,14 @@ const SQL_INSERT_REMATCH = SQL(`
const QUERY_LIST_GAMES = SQL(`
SELECT * FROM game_view
WHERE private=0 AND status=?
+ AND EXISTS ( SELECT 1 FROM players WHERE players.game_id = game_view.game_id AND user_id = game_view.owner_id )
ORDER BY mtime DESC
`);
const QUERY_LIST_GAMES_OF_TITLE = SQL(`
SELECT * FROM game_view
WHERE private=0 AND title_id=? AND status=?
+ AND EXISTS ( SELECT 1 FROM players WHERE players.game_id = game_view.game_id AND user_id = game_view.owner_id )
ORDER BY mtime DESC
LIMIT ?
`);
@@ -967,24 +971,44 @@ function is_solo(players) {
return players.every(p => p.user_id === players[0].user_id)
}
-function annotate_games(games, user_id) {
- for (let i = 0; i < games.length; ++i) {
- let game = games[i];
- let players = SQL_SELECT_PLAYERS_JOIN.all(game.game_id);
- game.player_names = players.map(p => {
- let name = p.name.replace(/ /g, '\xa0');
- return p.user_id > 0 ? `<a href="/user/${p.name}">${name}</a>` : name;
- }).join(", ");
- game.is_active = is_active(game, players, user_id);
- game.is_shared = is_shared(game, players, user_id);
- game.is_yours = false;
- if (user_id > 0)
- for (let i = 0; i < players.length; ++i)
- if (players[i].user_id === user_id)
- game.is_yours = 1;
- game.ctime = human_date(game.ctime);
- game.mtime = human_date(game.mtime);
+function format_options(options) {
+ function to_english(k) {
+ if (k === true) return 'yes';
+ if (k === false) return 'no';
+ return k.replace(/_/g, " ").replace(/^\w/, c => c.toUpperCase());
}
+ if (!options || options === '{}')
+ return "None";
+ options = JSON.parse(options);
+ return Object.entries(options||{}).map(([k,v]) => v === true ? to_english(k) : `${to_english(k)}=${to_english(v)}`).join(", ");
+}
+
+function annotate_game(game, user_id) {
+ let players = SQL_SELECT_PLAYERS_JOIN.all(game.game_id);
+ game.player_names = players.map(p => {
+ let name = p.name.replace(/ /g, '\xa0');
+ return p.user_id > 0 ? `<a href="/user/${p.name}">${name}</a>` : name;
+ }).join(", ");
+ game.options = format_options(game.options);
+ game.is_active = is_active(game, players, user_id);
+ game.is_shared = is_shared(game, players, user_id);
+ game.is_yours = false;
+ game.your_role = null;
+ if (user_id > 0) {
+ for (let i = 0; i < players.length; ++i) {
+ if (players[i].user_id === user_id) {
+ game.is_yours = 1;
+ game.your_role = players[i].role;
+ }
+ }
+ }
+ game.ctime = human_date(game.ctime);
+ game.mtime = human_date(game.mtime);
+}
+
+function annotate_games(games, user_id) {
+ for (let i = 0; i < games.length; ++i)
+ annotate_game(games[i], user_id);
}
app.get('/games', may_be_logged_in, function (req, res) {
@@ -1189,6 +1213,7 @@ app.get('/join/:game_id', must_be_logged_in, function (req, res) {
let game = SQL_SELECT_GAME_VIEW.get(game_id);
if (!game)
return res.status(404).send("Invalid game ID.");
+ annotate_game(game, req.user.user_id);
let roles = ROLES[game.title_id];
let players = SQL_SELECT_PLAYERS_JOIN.all(game_id);
let ready = (game.status === 0) && RULES[game.title_id].ready(game.scenario, game.options, players);
@@ -1310,7 +1335,7 @@ app.get('/play/:game_id/:role', may_be_logged_in, function (req, res) {
let title = SQL_SELECT_GAME_TITLE.get(game_id);
if (!title)
return res.redirect('/join/'+game_id);
- res.redirect('/'+title+'/play.html?game='+game_id+'&role='+role);
+ res.redirect('/'+title+'/play:'+game_id+':'+role);
});
app.get('/play/:game_id', may_be_logged_in, function (req, res) {
@@ -1322,8 +1347,27 @@ app.get('/play/:game_id', may_be_logged_in, function (req, res) {
return res.redirect('/join/'+game_id);
let role = SQL_SELECT_PLAYER_ROLE.get(game_id, user_id);
if (!role)
- return res.redirect('/'+title+'/play.html?game='+game_id+'&role=Observer');
- return res.redirect('/'+title+'/play.html?game='+game_id+'&role='+role);
+ role = "Observer";
+ res.redirect('/'+title+'/play:'+game_id+':'+role);
+});
+
+app.get('/:title_id/play\::game_id\::role', must_be_logged_in, function (req, res) {
+ let user_id = req.user ? req.user.user_id : 0;
+ let title_id = req.params.title_id
+ let game_id = req.params.game_id;
+ let role = req.params.role;
+ if (!SQL_AUTHORIZE_GAME_ROLE.get(title_id, game_id, role, user_id))
+ return res.send("You are not assigned that role.");
+ return res.sendFile(__dirname + '/public/' + title_id + '/play.html');
+});
+
+app.get('/:title_id/play\::game_id', may_be_logged_in, function (req, res) {
+ let title_id = req.params.title_id
+ let game_id = req.params.game_id;
+ let a_title = SQL_SELECT_GAME_TITLE.get(game_id);
+ if (a_title !== title_id)
+ return res.send("Invalid game ID.");
+ return res.sendFile(__dirname + '/public/' + title_id + '/play.html');
});
/*
diff --git a/views/games.ejs b/views/games.ejs
index 3b3f485..86a2502 100644
--- a/views/games.ejs
+++ b/views/games.ejs
@@ -2,41 +2,49 @@
<h2>Open</h2>
<table class="game">
-<tr><th>ID<th>Title<th>Scenario<th>Players<th>Description<th>Created<th>
-<% if (open_games.length > 0) { %>
-<% open_games.forEach((row) => { %>
+<tr><th>ID<th>Title<th>Scenario<th>Options<th>Players<th>Description<th>Created<th>
+<%_ if (open_games.length > 0) { _%>
+<%_ open_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="title"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
<td class="scenario"><%= row.scenario %>
-<td class="players"><%- row.player_names || row.owner_name %>
+<td class="options"><%- row.options %>
+<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.ctime %>
<td class="command"><a href="/join/<%= row.game_id %>">Join</a>
-<% }); } else { %>
-<tr><td colspan="7">No open games.
-<% } %>
+<%_ }); } else { _%>
+<tr><td colspan="8">No open games.
+<%_ } _%>
</table>
<h2>Active</h2>
<table class="game">
-<tr><th>ID<th>Title<th>Scenario<th>Players<th>Description<th>Changed<th>Active<th>
-<% if (active_games.length > 0) { %>
-<% active_games.forEach((row) => { %>
+<tr><th>ID<th>Title<th>Scenario<th>Options<th>Players<th>Description<th>Changed<th>Active<th>
+<%_ if (active_games.length > 0) { _%>
+<%_ active_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="title"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.mtime %>
<td class="role <%= row.is_active ? "is_active" : "" %>"><%= row.active %>
-<% if (row.is_yours) { %>
-<td class="command"><a href="/join/<%= row.game_id %>">Enter</a>
-<% } else { %>
-<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a>
-<% } %>
-<% }); } else { %>
-<tr><td colspan="8">No active games.
-<% } %>
+<%_
+ if (row.is_yours) {
+ if (row.is_shared) {
+ %><td class="command"><a href="/join/<%= row.game_id %>">View</a><%
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>:<%- row.your_role %>">View</a><%
+ }
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>">View</a><%
+ }
+_%>
+<%_ }); } else { _%>
+<tr><td colspan="9">No active games.
+<%_ } _%>
</table>
diff --git a/views/info.ejs b/views/info.ejs
index 7c98e21..4a515ff 100644
--- a/views/info.ejs
+++ b/views/info.ejs
@@ -1,4 +1,4 @@
-<%- include('header', { title: title.title_name, refresh: (user ? 300 : 0) }) %>
+<%- include('header', { title: title.title_name, refresh: (user ? 300 : 0) }) _%>
<img class="logo" src="/<%= title.title_id %>/cover.jpg">
<%- include('../public/' + title.title_id + '/about.html') %>
<p>
@@ -7,62 +7,78 @@ Read more about the game on
<h2>Open Games</h2>
<table class="game">
-<tr><th>ID<th>Scenario<th>Players<th>Description<th>Created<th>
-<% if (open_games.length > 0) { %>
-<% open_games.forEach((row) => { %>
+<tr><th>ID<th>Scenario<th>Options<th>Players<th>Description<th>Created<th>
+<%_ if (open_games.length > 0) { _%>
+<%_ open_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
-<td><%= row.scenario %>
-<td class="players"><%- row.player_names || `<a href="/user/${row.owner_name}">${row.owner_name}</a>` %>
+<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
+<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.ctime %>
<td class="command"><a href="/join/<%= row.game_id %>">Join</a>
-<% }); } else { %>
-<tr><td colspan="6">No open games.
-<% } %>
+<%_ }); } else { _%>
+<tr><td colspan="7">No open games.
+<%_ } _%>
</table>
<p>
<a href="/create/<%= title.title_id %>">Create a new game</a>.
-<% if (active_games.length > 0) { %>
+<%_ if (active_games.length > 0) { _%>
<h2>Active Games</h2>
<table class="game">
-<tr><th>ID<th>Scenario<th>Players<th>Description<th>Changed<th>Turn<th>
-<% active_games.forEach((row) => { %>
+<tr><th>ID<th>Scenario<th>Options<th>Players<th>Description<th>Changed<th>Turn<th>
+<%_ active_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.mtime %>
<td class="<%= row.is_active ? "role is_active" : "role" %>"><%= row.active %>
-<% if (row.is_yours) { %>
-<td class="command"><a href="/join/<%- row.game_id %>">Enter</a>
-<% } else { %>
-<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a>
-<% } %>
-<% }); %>
+<%_
+ if (row.is_yours) {
+ if (row.is_shared) {
+ %><td class="command"><a href="/join/<%= row.game_id %>">Play</a><%
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>:<%- row.your_role %>">Play</a><%
+ }
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>">View</a><%
+
+ }
+_%>
+<%_ }); _%>
</table>
-<% } %>
+<%_ } _%>
-<% if (finished_games.length > 0) { %>
+<%_ if (finished_games.length > 0) { _%>
<h2>Finished Games</h2>
<table class="game">
-<tr><th>ID<th>Scenario<th>Players<th>Description<th>Finished<th>Result<th>
-<% finished_games.forEach((row) => { %>
+<tr><th>ID<th>Scenario<th>Options<th>Players<th>Description<th>Finished<th>Result<th>
+<%_ finished_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.mtime %>
<td class="result"><%= row.result %>
-<% if (row.is_yours) { %>
-<td class="command"><a href="/join/<%= row.game_id %>">Enter</a>
-<% } else { %>
-<td class="command"><a href="/play/<%- row.game_id %>/Observer">View</a>
-<% } %>
-<% }); %>
+<%_
+ if (row.is_yours) {
+ if (row.is_shared) {
+ %><td class="command"><a href="/join/<%= row.game_id %>">View</a><%
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>:<%- row.your_role %>">View</a><%
+ }
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>">View</a><%
+ }
+_%>
+<%_ }); _%>
</table>
-<% } %>
+<%_ } _%>
diff --git a/views/join.ejs b/views/join.ejs
index fa33d5c..6558745 100644
--- a/views/join.ejs
+++ b/views/join.ejs
@@ -1,14 +1,4 @@
<%- include('header', { title: game.title_name }) -%>
-<%
-function to_english(k) {
- if (k === true) return 'yes';
- if (k === false) return 'no';
- return k.replace(/_/g, " ").replace(/^\w/, c => c.toUpperCase());
-}
-function format_options(options) {
- return Object.entries(options||{}).map(([k,v]) => v === true ? to_english(k) : `${to_english(k)}=${to_english(v)}`).join(", ");
-}
--%>
<style>
th, td { min-width: 10em; font-size: 16px; }
a.red { text-decoration: none; color: brown; font-size: 15px; }
@@ -35,13 +25,9 @@ Private: <%= game.private ? "yes" : "no" %>
<br>
Scenario: <%= game.scenario %>
<br>
-Options: <%- format_options(JSON.parse(game.options)) %>
+Options: <%= game.options %>
<br>
Description: <%= game.description || "No description." %>
-<br>
-Status: <span id="game_status"></span>
-<br>
-Result: <span id="game_result"></span>
<br clear=left>
diff --git a/views/profile.ejs b/views/profile.ejs
index 4d97486..a001e21 100644
--- a/views/profile.ejs
+++ b/views/profile.ejs
@@ -1,4 +1,4 @@
-<%- include('header', { title: "Rally the Troops!", refresh: (active_games.length > 0 ? 300 : 0) }) %>
+<%- include('header', { title: "Rally the Troops!", refresh: (active_games.length > 0 ? 300 : 0) }) _%>
<img class="logo avatar" src="<%= avatar %>" width="80" height="80">
<p>
Welcome, <%= user.name %>!
@@ -26,70 +26,79 @@ or <a href="/change_about">profile text</a>.
<br>&#xbb;
<a href="/logout">Logout</a>
-<% if (open_games.length > 0) { %>
+<%_ if (open_games.length > 0) { _%>
<h2>Open Games</h2>
<table class="game">
-<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Created<th>
-<% open_games.forEach((row) => { %>
+<tr><th>ID<th>Game<th>Scenario<th>Options<th>Players<th>Description<th>Created<th>
+<%_ open_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="name"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.ctime %>
<td class="command"><a href="/join/<%= row.game_id %>">Join</a>
-<% }); %>
+<%_ }); _%>
</table>
-<% } %>
+<%_ } _%>
-<% if (active_games.length > 0) { %>
+<%_ if (active_games.length > 0) { _%>
<h2>Active Games</h2>
<table class="game">
-<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Changed<th>Turn<th>
-<% active_games.forEach((row) => { %>
+<tr><th>ID<th>Game<th>Scenario<th>Options<th>Players<th>Description<th>Changed<th>Turn<th>
+<%_ active_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="title"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.mtime %>
-<%
+<%_
if (row.is_active) {
%><td class="role is_active"><%= row.active %><%
} else {
%><td class="role"><%= row.active %><%
}
if (row.is_shared) {
- %><td class="command"><a href="/join/<%= row.game_id %>">Enter</a><%
+ %><td class="command"><a href="/join/<%= row.game_id %>">Play</a><%
} else {
- %><td class="command"><a href="/play/<%= row.game_id %>">Play</a><%
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>:<%- row.your_role %>">Play</a><%
}
-%>
-<% }); %>
+_%>
+<%_ }); _%>
</table>
-<% } %>
+<%_ } _%>
-<% if (finished_games.length > 0) { %>
+<%_ if (finished_games.length > 0) { _%>
<h2>Finished Games</h2>
<table class="game">
-<tr><th>ID<th>Game<th>Scenario<th>Players<th>Description<th>Finished<th>Result<th>
-<% finished_games.forEach((row) => { %>
+<tr><th>ID<th>Game<th>Scenario<th>Options<th>Players<th>Description<th>Finished<th>Result<th>
+<%_ finished_games.forEach((row) => { _%>
<tr>
<td class="id"><%= row.game_id %>
<td class="title"><a href="/info/<%= row.title_id %>"><%= row.title_name %></a>
<td class="scenario"><%= row.scenario %>
+<td class="options"><%- row.options %>
<td class="players"><%- row.player_names %>
<td class="description"><%= row.description %>
<td class="time"><%= row.mtime %>
<td class="result"><%= row.result %>
-<td class="command"><a href="/join/<%= row.game_id %>">View</a>
-<% }); %>
+<%_
+ if (row.is_shared) {
+ %><td class="command"><a href="/join/<%= row.game_id %>">View</a><%
+ } else {
+ %><td class="command"><a href="/<%- row.title_id %>/play:<%- row.game_id %>:<%- row.your_role %>">View</a><%
+ }
+_%>
+<%_ }); _%>
</table>
-<% } %>
+<%_ } _%>
-<% if (open_games.length === 0 && active_games.length === 0 && finished_games.length === 0) { %>
+<%_ if (open_games.length === 0 && active_games.length === 0 && finished_games.length === 0) { _%>
<p>
You don't have any current or finished games.
-<% } %>
+<%_ } _%>