From 792f0d4c3169b92bb4aec8795eda2292a21330cf Mon Sep 17 00:00:00 2001 From: Zutatensuppe Date: Fri, 30 Apr 2021 21:00:30 +0200 Subject: [PATCH] split Game and GameStorage --- scripts/fix_image.js | 6 +-- scripts/fix_tiles.js | 7 ++-- server/Game.js | 88 +++------------------------------------- server/GameStorage.js | 93 +++++++++++++++++++++++++++++++++++++++++++ server/index.js | 7 ++-- 5 files changed, 108 insertions(+), 93 deletions(-) create mode 100644 server/GameStorage.js diff --git a/scripts/fix_image.js b/scripts/fix_image.js index 9b8fe26..8955f39 100755 --- a/scripts/fix_image.js +++ b/scripts/fix_image.js @@ -2,12 +2,12 @@ import GameCommon from '../common/GameCommon.js' import { logger } from '../common/Util.js' -import Game from '../server/Game.js' +import GameStorage from '../server/GameStorage.js' const log = logger('fix_image.js') function fix(gameId) { - Game.loadGameFromFile(gameId) + GameStorage.loadGame(gameId) let changed = false let imgUrl = GameCommon.getImageUrl(gameId) @@ -18,7 +18,7 @@ function fix(gameId) { changed = true } if (changed) { - Game.writeGameToFile(gameId) + GameStorage.persistGame(gameId) } } diff --git a/scripts/fix_tiles.js b/scripts/fix_tiles.js index 49865b0..709c545 100644 --- a/scripts/fix_tiles.js +++ b/scripts/fix_tiles.js @@ -1,12 +1,11 @@ import GameCommon from '../common/GameCommon.js' import { logger } from '../common/Util.js' -import Game from '../server/Game.js' - +import GameStorage from '../server/GameStorage.js' const log = logger('fix_tiles.js') function fix_tiles(gameId) { - Game.loadGameFromFile(gameId) + GameStorage.loadGame(gameId) let changed = false const tiles = GameCommon.getTilesSortedByZIndex(gameId) for (let tile of tiles) { @@ -28,7 +27,7 @@ function fix_tiles(gameId) { } } if (changed) { - Game.writeGameToFile(gameId) + GameStorage.persistGame(gameId) } } diff --git a/server/Game.js b/server/Game.js index b3cfccd..e275d44 100644 --- a/server/Game.js +++ b/server/Game.js @@ -1,60 +1,11 @@ -import fs from 'fs' import GameCommon from './../common/GameCommon.js' -import Util, { logger } from './../common/Util.js' +import Util from './../common/Util.js' import { Rng } from '../common/Rng.js' import GameLog from './GameLog.js' import { createPuzzle } from './Puzzle.js' import Protocol from '../common/Protocol.js' -import { DATA_DIR } from './Dirs.js' +import GameStorage from './GameStorage.js' -const log = logger('Game.js') - -function loadAllGames() { - const files = fs.readdirSync(DATA_DIR) - for (const f of files) { - const m = f.match(/^([a-z0-9]+)\.json$/) - if (!m) { - continue - } - const gameId = m[1] - loadGameFromFile(gameId) - } -} - -function loadGameFromFile(gameId) { - const file = `${DATA_DIR}/${gameId}.json` - const contents = fs.readFileSync(file, 'utf-8') - let game - try { - game = JSON.parse(contents) - } catch { - log.log(`[ERR] unable to load game from file ${file}`); - } - if (typeof game.puzzle.data.started === 'undefined') { - game.puzzle.data.started = Math.round(fs.statSync(file).ctimeMs) - } - if (typeof game.puzzle.data.finished === 'undefined') { - let unfinished = game.puzzle.tiles.map(Util.decodeTile).find(t => t.owner !== -1) - game.puzzle.data.finished = unfinished ? 0 : Util.timestamp() - } - if (!Array.isArray(game.players)) { - game.players = Object.values(game.players) - } - const gameObject = { - id: game.id, - rng: { - type: game.rng ? game.rng.type : '_fake_', - obj: game.rng ? Rng.unserialize(game.rng.obj) : new Rng(), - }, - puzzle: game.puzzle, - players: game.players, - evtInfos: {}, - scoreMode: game.scoreMode || GameCommon.SCORE_MODE_FINAL, - } - GameCommon.setGame(gameObject.id, gameObject) -} - -const changedGames = {} async function createGameObject(gameId, targetTiles, image, ts, scoreMode) { const seed = Util.hash(gameId + ' ' + ts) const rng = new Rng(seed) @@ -74,8 +25,7 @@ async function createGame(gameId, targetTiles, image, ts, scoreMode) { GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode) GameCommon.setGame(gameObject.id, gameObject) - - changedGames[gameId] = true + GameStorage.setDirty(gameId) } function addPlayer(gameId, playerId, ts) { @@ -88,7 +38,7 @@ function addPlayer(gameId, playerId, ts) { } GameCommon.addPlayer(gameId, playerId, ts) - changedGames[gameId] = true + GameStorage.setDirty(gameId) } function handleInput(gameId, playerId, input, ts) { @@ -97,40 +47,12 @@ function handleInput(gameId, playerId, input, ts) { GameLog.log(gameId, Protocol.LOG_HANDLE_INPUT, idx, input, diff) const ret = GameCommon.handleInput(gameId, playerId, input, ts) - changedGames[gameId] = true + GameStorage.setDirty(gameId) return ret } -function persistChangedGames() { - for (const gameId of Object.keys(changedGames)) { - writeGameToFile(gameId) - } -} - -function writeGameToFile(gameId) { - const game = GameCommon.get(gameId) - if (game.id in changedGames) { - delete changedGames[game.id] - } - fs.writeFileSync(`${DATA_DIR}/${game.id}.json`, JSON.stringify({ - id: game.id, - rng: { - type: game.rng.type, - obj: Rng.serialize(game.rng.obj), - }, - puzzle: game.puzzle, - players: game.players, - scoreMode: game.scoreMode, - })) - log.info(`[INFO] persisted game ${game.id}`) -} - export default { - loadGameFromFile, - writeGameToFile, createGameObject, - loadAllGames, - persistChangedGames, createGame, addPlayer, handleInput, diff --git a/server/GameStorage.js b/server/GameStorage.js new file mode 100644 index 0000000..5db0ecd --- /dev/null +++ b/server/GameStorage.js @@ -0,0 +1,93 @@ +import fs from 'fs' +import GameCommon from './../common/GameCommon.js' +import Util, { logger } from './../common/Util.js' +import { Rng } from '../common/Rng.js' +import { DATA_DIR } from './Dirs.js' +import Time from '../common/Time.js' + +const log = logger('GameStorage.js') + +const DIRTY_GAMES = {} +function setDirty(gameId) { + DIRTY_GAMES[gameId] = true +} +function setClean(gameId) { + delete DIRTY_GAMES[gameId] +} + +function loadGames() { + const files = fs.readdirSync(DATA_DIR) + for (const f of files) { + const m = f.match(/^([a-z0-9]+)\.json$/) + if (!m) { + continue + } + const gameId = m[1] + loadGame(gameId) + } +} + +function loadGame(gameId) { + const file = `${DATA_DIR}/${gameId}.json` + const contents = fs.readFileSync(file, 'utf-8') + let game + try { + game = JSON.parse(contents) + } catch { + log.log(`[ERR] unable to load game from file ${file}`); + } + if (typeof game.puzzle.data.started === 'undefined') { + game.puzzle.data.started = Math.round(fs.statSync(file).ctimeMs) + } + if (typeof game.puzzle.data.finished === 'undefined') { + let unfinished = game.puzzle.tiles.map(Util.decodeTile).find(t => t.owner !== -1) + game.puzzle.data.finished = unfinished ? 0 : Time.timestamp() + } + if (!Array.isArray(game.players)) { + game.players = Object.values(game.players) + } + const gameObject = { + id: game.id, + rng: { + type: game.rng ? game.rng.type : '_fake_', + obj: game.rng ? Rng.unserialize(game.rng.obj) : new Rng(), + }, + puzzle: game.puzzle, + players: game.players, + evtInfos: {}, + scoreMode: game.scoreMode || GameCommon.SCORE_MODE_FINAL, + } + GameCommon.setGame(gameObject.id, gameObject) +} + +function persistGames() { + for (const gameId of Object.keys(changedGames)) { + persistGame(gameId) + } +} + +function persistGame(gameId) { + const game = GameCommon.get(gameId) + if (game.id in DIRTY_GAMES) { + setClean(game.id) + } + fs.writeFileSync(`${DATA_DIR}/${game.id}.json`, JSON.stringify({ + id: game.id, + rng: { + type: game.rng.type, + obj: Rng.serialize(game.rng.obj), + }, + puzzle: game.puzzle, + players: game.players, + scoreMode: game.scoreMode, + })) + log.info(`[INFO] persisted game ${game.id}`) +} + +export default { + loadGames, + loadGame, + persistGames, + persistGame, + setDirty, +} diff --git a/server/index.js b/server/index.js index ebf174f..832d341 100644 --- a/server/index.js +++ b/server/index.js @@ -21,6 +21,7 @@ import { TEMPLATE_DIR, } from './Dirs.js' import GameCommon from '../common/GameCommon.js' +import GameStorage from './GameStorage.js' const log = logger('index.js') @@ -222,7 +223,7 @@ wss.on('message', async ({socket, data}) => { } }) -Game.loadAllGames() +GameStorage.loadGames() const server = app.listen( port, hostname, @@ -245,7 +246,7 @@ memoryUsageHuman() // persist games in fixed interval const persistInterval = setInterval(() => { log.log('Persisting games...') - Game.persistChangedGames() + GameStorage.persistGames() memoryUsageHuman() }, config.persistence.interval) @@ -257,7 +258,7 @@ const gracefulShutdown = (signal) => { clearInterval(persistInterval) log.log('persisting games...') - Game.persistChangedGames() + GameStorage.persistGames() log.log('shutting down webserver...') server.close()