split logs so that replay works for long games

This commit is contained in:
Zutatensuppe 2021-06-05 17:13:17 +02:00
parent 22f5ce0065
commit 514b3c6b22
10 changed files with 171 additions and 102 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>🧩 jigsaw.hyottoko.club</title>
<script type="module" crossorigin src="/assets/index.7efa4c6c.js"></script>
<script type="module" crossorigin src="/assets/index.ab1d6e0f.js"></script>
<link rel="modulepreload" href="/assets/vendor.684f7bc8.js">
<link rel="stylesheet" href="/assets/index.8f0efd0f.css">
</head>

View file

@ -3,8 +3,6 @@ import express from 'express';
import compression from 'compression';
import multer from 'multer';
import fs from 'fs';
import readline from 'readline';
import stream from 'stream';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import sizeOf from 'image-size';
@ -1249,6 +1247,7 @@ const PUBLIC_DIR = `${BASE_DIR}/build/public/`;
const DB_PATCHES_DIR = `${BASE_DIR}/src/dbpatches`;
const DB_FILE = `${BASE_DIR}/data/db.sqlite`;
const LINES_PER_LOG_FILE = 10000;
const POST_GAME_LOG_DURATION = 5 * Time.MIN;
const shouldLog = (finishTs, currentTs) => {
// when not finished yet, always log
@ -1260,54 +1259,52 @@ const shouldLog = (finishTs, currentTs) => {
const timeSinceGameEnd = currentTs - finishTs;
return timeSinceGameEnd <= POST_GAME_LOG_DURATION;
};
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`;
const filename = (gameId, offset) => `${DATA_DIR}/log_${gameId}-${offset}.log`;
const idxname = (gameId) => `${DATA_DIR}/log_${gameId}.idx.log`;
const create = (gameId) => {
const file = filename(gameId);
if (!fs.existsSync(file)) {
fs.appendFileSync(file, '');
const idxfile = idxname(gameId);
if (!fs.existsSync(idxfile)) {
const logfile = filename(gameId, 0);
fs.appendFileSync(logfile, "");
fs.appendFileSync(idxfile, JSON.stringify({
total: 0,
currentFile: logfile,
perFile: LINES_PER_LOG_FILE,
}));
}
};
const exists = (gameId) => {
const file = filename(gameId);
return fs.existsSync(file);
const idxfile = idxname(gameId);
return fs.existsSync(idxfile);
};
const _log = (gameId, ...args) => {
const file = filename(gameId);
if (!fs.existsSync(file)) {
const idxfile = idxname(gameId);
if (!fs.existsSync(idxfile)) {
return;
}
const str = JSON.stringify(args);
fs.appendFileSync(file, str + "\n");
const idx = JSON.parse(fs.readFileSync(idxfile, 'utf-8'));
idx.total++;
fs.appendFileSync(idx.currentFile, JSON.stringify(args) + "\n");
// prepare next log file
if (idx.total % idx.perFile === 0) {
const logfile = filename(gameId, idx.total);
fs.appendFileSync(logfile, "");
idx.currentFile = logfile;
}
fs.writeFileSync(idxfile, JSON.stringify(idx));
};
const get = async (gameId, offset = 0, size = 10000) => {
const file = filename(gameId);
const get = (gameId, offset = 0) => {
const idxfile = idxname(gameId);
if (!fs.existsSync(idxfile)) {
return [];
}
const file = filename(gameId, offset);
if (!fs.existsSync(file)) {
return [];
}
return new Promise((resolve) => {
const instream = fs.createReadStream(file);
const outstream = new stream.Writable();
const rl = readline.createInterface(instream, outstream);
const lines = [];
let i = -1;
rl.on('line', (line) => {
if (!line) {
// skip empty
return;
}
i++;
if (offset > i) {
return;
}
if (offset + size <= i) {
rl.close();
return;
}
lines.push(JSON.parse(line));
});
rl.on('close', () => {
resolve(lines);
});
const log = fs.readFileSync(file, 'utf-8').split("\n");
return log.map(line => {
return JSON.parse(line);
});
};
var GameLog = {
@ -2061,7 +2058,7 @@ app.get('/api/replay-data', async (req, res) => {
res.status(404).send({ reason: 'no log found' });
return;
}
const log = await GameLog.get(gameId, offset, size);
const log = GameLog.get(gameId, offset);
let game = null;
if (offset === 0) {
// also need the game