log only 5 min after game end + some type hinting :P

This commit is contained in:
Zutatensuppe 2021-05-31 20:05:41 +02:00
parent c2da0759b9
commit 870f827e49
9 changed files with 108 additions and 56 deletions

View file

@ -1186,6 +1186,17 @@ const PUBLIC_DIR = `${BASE_DIR}/build/public/`;
const DB_PATCHES_DIR = `${BASE_DIR}/src/dbpatches`;
const DB_FILE = `${BASE_DIR}/data/db.sqlite`;
const POST_GAME_LOG_DURATION = 5 * Time.MIN;
const shouldLog = (finishTs, currentTs) => {
// when not finished yet, always log
if (!finishTs) {
return true;
}
// in finished games, log max POST_GAME_LOG_DURATION after
// the game finished, to record winning dance moves etc :P
const timeSinceGameEnd = currentTs - finishTs;
return timeSinceGameEnd <= POST_GAME_LOG_DURATION;
};
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`;
const create = (gameId) => {
const file = filename(gameId);
@ -1237,6 +1248,7 @@ const get = async (gameId, offset = 0, size = 10000) => {
});
};
var GameLog = {
shouldLog,
create,
exists,
log: _log,
@ -1689,6 +1701,7 @@ async function createGame(gameId, targetTiles, image, ts, scoreMode) {
GameStorage.setDirty(gameId);
}
function addPlayer(gameId, playerId, ts) {
if (GameLog.shouldLog(GameCommon.getFinishTs(gameId), ts)) {
const idx = GameCommon.getPlayerIndexById(gameId, playerId);
const diff = ts - GameCommon.getStartTs(gameId);
if (idx === -1) {
@ -1697,13 +1710,16 @@ function addPlayer(gameId, playerId, ts) {
else {
GameLog.log(gameId, Protocol.LOG_UPDATE_PLAYER, idx, diff);
}
}
GameCommon.addPlayer(gameId, playerId, ts);
GameStorage.setDirty(gameId);
}
function handleInput(gameId, playerId, input, ts) {
if (GameLog.shouldLog(GameCommon.getFinishTs(gameId), ts)) {
const idx = GameCommon.getPlayerIndexById(gameId, playerId);
const diff = ts - GameCommon.getStartTs(gameId);
GameLog.log(gameId, Protocol.LOG_HANDLE_INPUT, idx, input, diff);
}
const ret = GameCommon.handleInput(gameId, playerId, input, ts);
GameStorage.setDirty(gameId);
return ret;
@ -2069,6 +2085,8 @@ wss.on('message', async ({ socket, data }) => {
const proto = socket.protocol.split('|');
const clientId = proto[0];
const gameId = proto[1];
// TODO: maybe handle different types of data
// (but atm only string comes through)
const msg = JSON.parse(data);
const msgType = msg[0];
switch (msgType) {
@ -2152,12 +2170,12 @@ const gracefulShutdown = (signal) => {
process.exit();
};
// used by nodemon
process.once('SIGUSR2', function () {
process.once('SIGUSR2', () => {
gracefulShutdown('SIGUSR2');
});
process.once('SIGINT', function () {
process.once('SIGINT', () => {
gracefulShutdown('SIGINT');
});
process.once('SIGTERM', function () {
process.once('SIGTERM', () => {
gracefulShutdown('SIGTERM');
});

View file

@ -15,6 +15,7 @@ export type Change = Array<any>
export type GameEvent = Array<any>
export type ServerEvent = Array<any>
export type ClientEvent = Array<any>
export type EncodedPlayer = FixedLengthArray<[

View file

@ -29,8 +29,11 @@ const pad = (x: number, pad: string): string => {
return pad.substr(0, pad.length - str.length) + str
}
export const logger = (...pre: Array<any>) => {
const log = (m: 'log'|'info'|'error') => (...args: Array<any>) => {
type LogArgs = Array<any>
type LogFn = (...args: LogArgs) => void
export const logger = (...pre: string[]): { log: LogFn, error: LogFn, info: LogFn } => {
const log = (m: 'log'|'info'|'error') => (...args: LogArgs): void => {
const d = new Date()
const hh = pad(d.getHours(), '00')
const mm = pad(d.getMinutes(), '00')

View file

@ -1,6 +1,6 @@
"use strict"
import { ClientEvent, EncodedGame, GameEvent, ReplayData } from '../common/Types'
import { ClientEvent, EncodedGame, GameEvent, ReplayData, ServerEvent } from '../common/Types'
import Util, { logger } from '../common/Util'
import Protocol from './../common/Protocol'
@ -17,8 +17,8 @@ const CONN_STATE_CLOSED = 4 // not connected (closed on purpose)
let ws: WebSocket
let missedMessages: Array<any> = []
let changesCallback = (msg: Array<any>) => {
let missedMessages: ServerEvent[] = []
let changesCallback = (msg: ServerEvent) => {
missedMessages.push(msg)
}
@ -27,7 +27,7 @@ let connectionStateChangeCallback = (state: number) => {
missedStateChanges.push(state)
}
function onServerChange(callback: (msg: Array<any>) => void): void {
function onServerChange(callback: (msg: ServerEvent) => void): void {
changesCallback = callback
for (const missedMessage of missedMessages) {
changesCallback(missedMessage)
@ -77,8 +77,8 @@ function connect(
setConnectionState(CONN_STATE_CONNECTED)
send([Protocol.EV_CLIENT_INIT])
}
ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
ws.onmessage = (e: MessageEvent) => {
const msg: ServerEvent = JSON.parse(e.data)
const msgType = msg[0]
if (msgType === Protocol.EV_SERVER_INIT) {
const game = msg[1]
@ -102,7 +102,7 @@ function connect(
throw `[ 2021-05-15 onerror ]`
}
ws.onclose = (e) => {
ws.onclose = (e: CloseEvent) => {
if (e.code === CODE_CUSTOM_DISCONNECT || e.code === CODE_GOING_AWAY) {
setConnectionState(CONN_STATE_CLOSED)
} else {

View file

@ -21,6 +21,7 @@ import {
ReplayData,
Timestamp,
GameEvent,
ServerEvent,
} from '../common/Types'
declare global {
interface Window {
@ -485,7 +486,7 @@ export async function main(
}
if (MODE === MODE_PLAY) {
Communication.onServerChange((msg) => {
Communication.onServerChange((msg: ServerEvent) => {
const msgType = msg[0]
const evClientId = msg[1]
const evClientSeq = msg[2]

View file

@ -1,12 +1,14 @@
import GameCommon from './../common/GameCommon'
import { Change, Game, Input, ScoreMode, Timestamp } from './../common/Types'
import Util from './../common/Util'
import Util, { logger } from './../common/Util'
import { Rng } from './../common/Rng'
import GameLog from './GameLog'
import { createPuzzle, PuzzleCreationImageInfo } from './Puzzle'
import Protocol from './../common/Protocol'
import GameStorage from './GameStorage'
const log = logger('Game.ts')
async function createGameObject(
gameId: string,
targetTiles: number,
@ -49,6 +51,7 @@ async function createGame(
}
function addPlayer(gameId: string, playerId: string, ts: Timestamp): void {
if (GameLog.shouldLog(GameCommon.getFinishTs(gameId), ts)) {
const idx = GameCommon.getPlayerIndexById(gameId, playerId)
const diff = ts - GameCommon.getStartTs(gameId)
if (idx === -1) {
@ -56,6 +59,7 @@ function addPlayer(gameId: string, playerId: string, ts: Timestamp): void {
} else {
GameLog.log(gameId, Protocol.LOG_UPDATE_PLAYER, idx, diff)
}
}
GameCommon.addPlayer(gameId, playerId, ts)
GameStorage.setDirty(gameId)
@ -67,9 +71,11 @@ function handleInput(
input: Input,
ts: Timestamp
): Array<Change> {
if (GameLog.shouldLog(GameCommon.getFinishTs(gameId), ts)) {
const idx = GameCommon.getPlayerIndexById(gameId, playerId)
const diff = ts - GameCommon.getStartTs(gameId)
GameLog.log(gameId, Protocol.LOG_HANDLE_INPUT, idx, input, diff)
}
const ret = GameCommon.handleInput(gameId, playerId, input, ts)
GameStorage.setDirty(gameId)

View file

@ -1,11 +1,27 @@
import fs from 'fs'
import readline from 'readline'
import stream from 'stream'
import Time from '../common/Time'
import { Timestamp } from '../common/Types'
import { logger } from './../common/Util'
import { DATA_DIR } from './../server/Dirs'
const log = logger('GameLog.js')
const POST_GAME_LOG_DURATION = 5 * Time.MIN
const shouldLog = (finishTs: Timestamp, currentTs: Timestamp): boolean => {
// when not finished yet, always log
if (!finishTs) {
return true
}
// in finished games, log max POST_GAME_LOG_DURATION after
// the game finished, to record winning dance moves etc :P
const timeSinceGameEnd = currentTs - finishTs
return timeSinceGameEnd <= POST_GAME_LOG_DURATION
}
const filename = (gameId: string) => `${DATA_DIR}/log_${gameId}.log`
const create = (gameId: string): void => {
@ -66,6 +82,7 @@ const get = async (
}
export default {
shouldLog,
create,
exists,
log: _log,

View file

@ -56,7 +56,7 @@ class WebSocketServer {
socket.close()
return
}
socket.on('message', (data: any) => {
socket.on('message', (data: WebSocket.Data) => {
log.log(`ws`, socket.protocol, data)
this.evt.dispatch('message', {socket, data})
})

View file

@ -19,7 +19,7 @@ import {
UPLOAD_DIR,
} from './Dirs'
import GameCommon from '../common/GameCommon'
import { Game as GameType, GameSettings, ScoreMode } from '../common/Types'
import { ServerEvent, Game as GameType, GameSettings, ScoreMode } from '../common/Types'
import GameStorage from './GameStorage'
import Db from './Db'
@ -57,14 +57,14 @@ const storage = multer.diskStorage({
})
const upload = multer({storage}).single('file');
app.get('/api/conf', (req, res) => {
app.get('/api/conf', (req, res): void => {
res.send({
WS_ADDRESS: config.ws.connectstring,
})
})
app.get('/api/replay-data', async (req, res) => {
const q = req.query as any
app.get('/api/replay-data', async (req, res): Promise<void> => {
const q: Record<string, any> = req.query
const offset = parseInt(q.offset, 10) || 0
if (offset < 0) {
res.status(400).send({ reason: 'bad offset' })
@ -95,8 +95,8 @@ app.get('/api/replay-data', async (req, res) => {
res.send({ log, game: game ? Util.encodeGame(game) : null })
})
app.get('/api/newgame-data', (req, res) => {
const q = req.query as any
app.get('/api/newgame-data', (req, res): void => {
const q: Record<string, any> = req.query
const tagSlugs: string[] = q.tags ? q.tags.split(',') : []
res.send({
images: Images.allImagesFromDb(db, tagSlugs, q.sort),
@ -104,10 +104,10 @@ app.get('/api/newgame-data', (req, res) => {
})
})
app.get('/api/index-data', (req, res) => {
app.get('/api/index-data', (req, res): void => {
const ts = Time.timestamp()
const games = [
...GameCommon.getAllGames().map((game: any) => ({
...GameCommon.getAllGames().map((game: GameType) => ({
id: game.id,
hasReplay: GameLog.exists(game.id),
started: GameCommon.getStartTs(game.id),
@ -131,7 +131,7 @@ interface SaveImageRequestData {
tags: string[]
}
const setImageTags = (db: Db, imageId: number, tags: string[]) => {
const setImageTags = (db: Db, imageId: number, tags: string[]): void => {
tags.forEach((tag: string) => {
const slug = Util.slug(tag)
const id = db.upsert('categories', { slug, title: tag }, { slug }, 'id')
@ -144,7 +144,7 @@ const setImageTags = (db: Db, imageId: number, tags: string[]) => {
})
}
app.post('/api/save-image', express.json(), (req, res) => {
app.post('/api/save-image', express.json(), (req, res): void => {
const data = req.body as SaveImageRequestData
db.update('images', {
title: data.title,
@ -160,8 +160,8 @@ app.post('/api/save-image', express.json(), (req, res) => {
res.send({ ok: true })
})
app.post('/api/upload', (req, res) => {
upload(req, res, async (err: any) => {
app.post('/api/upload', (req, res): void => {
upload(req, res, async (err: any): Promise<void> => {
if (err) {
log.log(err)
res.status(400).send("Something went wrong!");
@ -189,7 +189,7 @@ app.post('/api/upload', (req, res) => {
})
})
app.post('/api/newgame', express.json(), async (req, res) => {
app.post('/api/newgame', express.json(), async (req, res): Promise<void> => {
const gameSettings = req.body as GameSettings
log.log(gameSettings)
const gameId = Util.uniqId()
@ -211,13 +211,15 @@ app.use('/', express.static(PUBLIC_DIR))
const wss = new WebSocketServer(config.ws);
const notify = (data: any, sockets: Array<WebSocket>) => {
const notify = (data: ServerEvent, sockets: Array<WebSocket>): void => {
for (const socket of sockets) {
wss.notifyOne(data, socket)
}
}
wss.on('close', async ({socket} : {socket: WebSocket}) => {
wss.on('close', async (
{socket} : { socket: WebSocket }
): Promise<void> => {
try {
const proto = socket.protocol.split('|')
// const clientId = proto[0]
@ -228,12 +230,16 @@ wss.on('close', async ({socket} : {socket: WebSocket}) => {
}
})
wss.on('message', async ({socket, data} : { socket: WebSocket, data: any }) => {
wss.on('message', async (
{socket, data} : { socket: WebSocket, data: WebSocket.Data }
): Promise<void> => {
try {
const proto = socket.protocol.split('|')
const clientId = proto[0]
const gameId = proto[1]
const msg = JSON.parse(data)
// TODO: maybe handle different types of data
// (but atm only string comes through)
const msg = JSON.parse(data as string)
const msgType = msg[0]
switch (msgType) {
case Protocol.EV_CLIENT_INIT: {
@ -303,7 +309,7 @@ const server = app.listen(
wss.listen()
const memoryUsageHuman = () => {
const memoryUsageHuman = (): void => {
const totalHeapSize = v8.getHeapStatistics().total_available_size
const totalHeapSizeInGB = (totalHeapSize / 1024 / 1024 / 1024).toFixed(2)
@ -322,7 +328,7 @@ const persistInterval = setInterval(() => {
memoryUsageHuman()
}, config.persistence.interval)
const gracefulShutdown = (signal: any) => {
const gracefulShutdown = (signal: string): void => {
log.log(`${signal} received...`)
log.log('clearing persist interval...')
@ -342,14 +348,14 @@ const gracefulShutdown = (signal: any) => {
}
// used by nodemon
process.once('SIGUSR2', function () {
process.once('SIGUSR2', (): void => {
gracefulShutdown('SIGUSR2')
})
process.once('SIGINT', function () {
process.once('SIGINT', (): void => {
gracefulShutdown('SIGINT')
})
process.once('SIGTERM', function () {
process.once('SIGTERM', (): void => {
gracefulShutdown('SIGTERM')
})