log only 5 min after game end + some type hinting :P
This commit is contained in:
parent
c2da0759b9
commit
870f827e49
9 changed files with 108 additions and 56 deletions
|
|
@ -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');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<[
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue