summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2021-10-23 23:15:45 +0200
committerTor Andersson <tor@ccxvii.net>2021-10-28 17:36:03 +0200
commit35c3df0bf9209e79fb93875b1fc3e5afee032028 (patch)
tree3a55991699a22fa7f37835623f00110e41d6e68d
parent3d172bf1b9b323d838c3756d85f75b943aefff0f (diff)
downloadserver-35c3df0bf9209e79fb93875b1fc3e5afee032028.tar.gz
Add PRNG seed to game state.
Log all game actions to a table so they can be replayed.
-rw-r--r--server.js24
-rw-r--r--tools/rerun.js24
-rw-r--r--tools/sql/schema.txt9
3 files changed, 54 insertions, 3 deletions
diff --git a/server.js b/server.js
index cc81a18..321ff44 100644
--- a/server.js
+++ b/server.js
@@ -14,6 +14,10 @@ const SQLiteStore = require('./connect-better-sqlite3')(express_session);
require('dotenv').config();
+function random_seed() {
+ return crypto.randomInt(1, 0x7ffffffe);
+}
+
const SESSION_SECRET = "Caesar has a big head!";
const MAX_OPEN_GAMES = 5;
@@ -922,7 +926,7 @@ app.get('/part/:game_id/:role', must_be_logged_in, function (req, res) {
function assign_random_roles(game, players) {
function pick_random_item(list) {
- let k = Math.floor(Math.random() * list.length);
+ let k = crypto.randomInt(list.length);
let r = list[k];
list.splice(k, 1);
return r;
@@ -952,7 +956,9 @@ app.get('/start/:game_id', must_be_logged_in, function (req, res) {
assign_random_roles(game, players);
update_join_clients_players(game_id);
}
- let state = RULES[game.title_id].setup(game.scenario, players);
+ let seed = random_seed();
+ let state = RULES[game.title_id].setup(seed, game.scenario, players);
+ put_replay(game_id, null, 'setup', [seed, game.scenario, players]);
QUERY_START_GAME.run(JSON.stringify(state), state.active, game_id);
let is_solo = players.every(p => p.user_id === players[0].user_id);
if (is_solo)
@@ -1194,6 +1200,14 @@ function put_game_state(game_id, state, old_active) {
mail_your_turn_notification_to_offline_users(game_id, old_active, state.active);
}
+const QUERY_INSERT_REPLAY = db.prepare("INSERT INTO replay ( game_id, time, role, action, arguments ) VALUES ( ?, datetime('now'), ?, ?, ? )");
+
+function put_replay(game_id, role, action, args) {
+ if (args !== undefined && args !== null)
+ args = JSON.stringify(args);
+ QUERY_INSERT_REPLAY.run(game_id, role, action, args);
+}
+
function on_action(socket, action, arg) {
SLOG(socket, "--> ACTION", action, arg);
try {
@@ -1201,6 +1215,7 @@ function on_action(socket, action, arg) {
let old_active = state.active;
socket.rules.action(state, socket.role, action, arg);
put_game_state(socket.game_id, state, old_active);
+ put_replay(socket.game_id, socket.role, action, arg);
} catch (err) {
console.log(err);
return socket.emit('error', err.toString());
@@ -1214,6 +1229,7 @@ function on_resign(socket) {
let old_active = state.active;
socket.rules.resign(state, socket.role);
put_game_state(socket.game_id, state, old_active);
+ put_replay(socket.game_id, socket.role, 'resign', null);
} catch (err) {
console.log(err);
return socket.emit('error', err.toString());
@@ -1369,7 +1385,9 @@ io.on('connection', (socket) => {
socket.on('restore', (state) => on_restore(socket, state));
socket.on('restart', (scenario) => {
try {
- let state = socket.rules.setup(scenario, players);
+ let seed = random_seed();
+ let state = socket.rules.setup(seed, scenario, players);
+ put_replay(socket.game_id, null, 'setup', [seed, scenario, players]);
for (let other of clients[socket.game_id]) {
other.log_length = 0;
send_state(other, state);
diff --git a/tools/rerun.js b/tools/rerun.js
new file mode 100644
index 0000000..d1534e6
--- /dev/null
+++ b/tools/rerun.js
@@ -0,0 +1,24 @@
+const sqlite3 = require('better-sqlite3');
+
+let db = new sqlite3("./db");
+let game_id = process.argv[2] | 0;
+let title_id = db.prepare("SELECT title_id FROM games WHERE game_id = ?").pluck().get(game_id);
+let rules = require("./public/" + title_id + "/rules.js");
+
+console.log("// TITLE", title_id)
+let log = db.prepare("SELECT * FROM game_log WHERE game_id = ?").all(game_id);
+let game = null;
+log.forEach(item => {
+ let args = JSON.parse(item.arguments);
+ if (item.action === 'setup') {
+ console.log("// SETUP", item.arguments)
+ game = rules.setup(args[0], args[1], args[2]);
+ } else if (item.action === 'resign') {
+ console.log("// RESIGN", item.role);
+ game = rules.resign(game, item.role);
+ } else {
+ console.log("// ACTION", item.role, item.action, item.arguments);
+ game = rules.action(game, item.role, item.action, args);
+ }
+ console.log(JSON.stringify(game));
+});
diff --git a/tools/sql/schema.txt b/tools/sql/schema.txt
index e731c8c..1623ec3 100644
--- a/tools/sql/schema.txt
+++ b/tools/sql/schema.txt
@@ -55,6 +55,14 @@ CREATE TABLE IF NOT EXISTS games (
state TEXT
);
+CREATE TABLE IF NOT EXISTS replay (
+ game_id INTEGER,
+ time TIMESTAMP,
+ role TEXT,
+ action TEXT,
+ arguments TEXT
+);
+
CREATE TABLE IF NOT EXISTS chats (
game_id INTEGER PRIMARY KEY,
time TIMESTAMP,
@@ -110,6 +118,7 @@ BEGIN
DELETE FROM players WHERE game_id = old.game_id;
DELETE FROM notifications WHERE game_id = old.game_id;
DELETE FROM chats WHERE game_id = old.game_id;
+ DELETE FROM replay WHERE game_id = old.game_id;
END;
DROP VIEW IF EXISTS player_view;