diff --git a/docs/mongo-db-schema.odg b/docs/mongo-db-schema.odg index 26737ec..90557e0 100644 Binary files a/docs/mongo-db-schema.odg and b/docs/mongo-db-schema.odg differ diff --git a/server/models/logs/budget.js b/server/models/logs/budget.js index ad8c8f2..328e449 100644 --- a/server/models/logs/budget.js +++ b/server/models/logs/budget.js @@ -32,7 +32,7 @@ const LogBudgetSchema = new Schema({ }, }, { collection: 'logBudget', - versionKey: false + versionKey: false, }); // optional more indices LogBudgetSchema.index({war: 1}); diff --git a/server/models/logs/flag.js b/server/models/logs/flag.js index aa1b5b7..bfe9bf1 100644 --- a/server/models/logs/flag.js +++ b/server/models/logs/flag.js @@ -28,7 +28,7 @@ const LogFlagSchema = new Schema({ }, }, { collection: 'logFlag', - versionKey: false + versionKey: false, }); // optional more indices LogFlagSchema.index({war: 1, player: 1}); diff --git a/server/models/logs/kill.js b/server/models/logs/kill.js index 70e10a0..90c6370 100644 --- a/server/models/logs/kill.js +++ b/server/models/logs/kill.js @@ -40,7 +40,7 @@ const LogKillSchema = new Schema({ }, }, { collection: 'logKill', - versionKey: false + versionKey: false, }); // optional more indices LogKillSchema.index({war: 1, shooter: 1, target: 1}); diff --git a/server/models/logs/player-count.js b/server/models/logs/player-count.js index 9402c86..13e7bb7 100644 --- a/server/models/logs/player-count.js +++ b/server/models/logs/player-count.js @@ -27,7 +27,7 @@ const LogPlayerCountSchema = new Schema({ }, }, { collection: 'logPlayerCount', - versionKey: false + versionKey: false, }); // optional more indices LogPlayerCountSchema.index({war: 1}); diff --git a/server/models/logs/points.js b/server/models/logs/points.js index 83afe52..6485b69 100644 --- a/server/models/logs/points.js +++ b/server/models/logs/points.js @@ -32,7 +32,7 @@ const LogKillSchema = new Schema({ }, }, { collection: 'logPoints', - versionKey: false + versionKey: false, }); // optional more indices LogKillSchema.index({war: 1, shooter: 1, target: 1}); diff --git a/server/models/logs/respawn.js b/server/models/logs/respawn.js index 386755a..e0f1652 100644 --- a/server/models/logs/respawn.js +++ b/server/models/logs/respawn.js @@ -19,7 +19,7 @@ const LogRespawnSchema = new Schema({ }, }, { collection: 'logRespawn', - versionKey: false + versionKey: false, }); // optional more indices LogRespawnSchema.index({war: 1, player: 1}); diff --git a/server/models/logs/revive.js b/server/models/logs/revive.js index 93f993e..aa6378b 100644 --- a/server/models/logs/revive.js +++ b/server/models/logs/revive.js @@ -32,7 +32,7 @@ const LogReviveSchema = new Schema({ }, }, { collection: 'logRevive', - versionKey: false + versionKey: false, }); // optional more indices LogReviveSchema.index({war: 1, medic: 1}); diff --git a/server/models/logs/server-fps.js b/server/models/logs/server-fps.js index 99aa0ae..01537b4 100644 --- a/server/models/logs/server-fps.js +++ b/server/models/logs/server-fps.js @@ -13,6 +13,18 @@ const LogServerFpsSchema = new Schema({ type: String, required: true, }, + singleAvgFps: { + type: Number, + get: (v) => Math.round(v), + set: (v) => Math.round(v), + required: true, + }, + singleMinFps: { + type: Number, + get: (v) => Math.round(v), + set: (v) => Math.round(v), + required: true, + }, avgFps: [{ type: Number, get: (v) => Math.round(v), @@ -27,7 +39,7 @@ const LogServerFpsSchema = new Schema({ }], }, { collection: 'logServerFps', - versionKey: false + versionKey: false, }); // optional more indices LogServerFpsSchema.index({war: 1}); diff --git a/server/models/logs/transport.js b/server/models/logs/transport.js index ccf4d89..6bddc4d 100644 --- a/server/models/logs/transport.js +++ b/server/models/logs/transport.js @@ -34,7 +34,7 @@ const LogTransportSchema = new Schema({ }, }, { collection: 'logTransport', - versionKey: false + versionKey: false, }); // optional more indices LogTransportSchema.index({war: 1, driver: 1}); diff --git a/server/models/logs/vehicle.js b/server/models/logs/vehicle.js index c898cfa..d796636 100644 --- a/server/models/logs/vehicle.js +++ b/server/models/logs/vehicle.js @@ -41,7 +41,7 @@ const LogVehicleKillSchema = new Schema({ }, }, { collection: 'logVehicle', - versionKey: false + versionKey: false, }); // optional more indices LogVehicleKillSchema.index({war: 1, shooter: 1, target: 1}); diff --git a/server/models/player.js b/server/models/player.js index a407a8a..85bd6f3 100644 --- a/server/models/player.js +++ b/server/models/player.js @@ -82,6 +82,11 @@ const PlayerSchema = new Schema({ get: (v) => Math.round(v), set: (v) => Math.round(v), }, + performance: { + type: mongoose.Schema.Types.ObjectId, + ref: 'LogServerFpsSchema', + required: true, + }, }, { collection: 'player', timestamps: {createdAt: 'timestamp'}, diff --git a/server/routes/wars.js b/server/routes/wars.js index 9bb1fac..62984b4 100644 --- a/server/routes/wars.js +++ b/server/routes/wars.js @@ -34,6 +34,7 @@ const LogFlagModel = require('../models/logs/flag'); const LogBudgetModel = require('../models/logs/budget'); const LogPointsModel = require('../models/logs/points'); const LogPlayerCountModel = require('../models/logs/player-count'); +const LogServerFpsModel = require('../models/logs/server-fps'); // util const genericPatch = require('./_generic').genericPatch; @@ -59,25 +60,20 @@ wars.route('/') }) .post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => { - const body = req.body; - const warBody = new WarModel(body); + const body = req.body; + const warBody = new WarModel(body); - if (req.file) { - fs.readFile(req.file.buffer, (file, err) => { - if (err) { - return next(err); - } - const lineArray = file.toString().split('\n'); - const statsResult = parseWarLog(lineArray, warBody); - statsResult.war.save((err, war) => { + if (req.file) { + fs.readFile(req.file.buffer, (file, err) => { if (err) { return next(err); } - PlayerModel.create(statsResult.players, (err) => { + const lineArray = file.toString().split('\n'); + const statsResult = parseWarLog(lineArray, warBody); + statsResult.war.save((err, war) => { if (err) { return next(err); } - LogKillModel.create(statsResult.kills, () => { LogVehicleKillModel.create(statsResult.vehicles, () => { LogRespawnModel.create(statsResult.respawn, () => { @@ -87,27 +83,43 @@ wars.route('/') LogTransportModel.create(statsResult.transport, () => { LogPlayerCountModel.create(statsResult.playerCount, () => { LogPointsModel.create(statsResult.points, () => { - const folderName = resourceLocation.concat(war._id); - mkdirp(folderName, (err) => { - if (err) return next(err); - - // save clean log file - const cleanFile = fs.createWriteStream(folderName + '/clean.log'); - statsResult.clean.forEach((cleanLine) => { - cleanFile.write(cleanLine + '\n\n'); + LogServerFpsModel.create(statsResult.serverFps, (err, serverPerformanceEntries) => { + serverPerformanceEntries.forEach((entry) => { + const idx = statsResult.players + .findIndex((player) => player.name === entry.entityName); + if (idx !== -1) { + const player = statsResult.players[idx]; + player['performance'] = entry._id; + statsResult.players[idx] = player; + } }); - cleanFile.end(); + PlayerModel.create(statsResult.players, (err) => { + if (err) { + return next(err); + } + const folderName = resourceLocation.concat(war._id); + mkdirp(folderName, (err) => { + if (err) return next(err); - // save raw log file - const rawFile = fs.createWriteStream(folderName + '/war.log'); - lineArray.forEach((rawLine) => { - rawFile.write(rawLine + '\n'); + // save clean log file + const cleanFile = fs.createWriteStream(folderName + '/clean.log'); + statsResult.clean.forEach((cleanLine) => { + cleanFile.write(cleanLine + '\n\n'); + }); + cleanFile.end(); + + // save raw log file + const rawFile = fs.createWriteStream(folderName + '/war.log'); + lineArray.forEach((rawLine) => { + rawFile.write(rawLine + '\n'); + }); + rawFile.end(); + + res.status(codes.created); + res.locals.items = war; + next(); + }); }); - rawFile.end(); - - res.status(codes.created); - res.locals.items = war; - next(); }); }); }); @@ -120,13 +132,13 @@ wars.route('/') }); }); }); - }); - } else { - const err = new Error('no Logfile provided'); - err.status = codes.wrongmediasend; - next(err); + } else { + const err = new Error('no Logfile provided'); + err.status = codes.wrongmediasend; + next(err); + } } - }) + ) .all( routerHandling.httpMethodNotAllowed diff --git a/server/tools/log-parse-tool.js b/server/tools/log-parse-tool.js index bdaaf63..e51ae68 100644 --- a/server/tools/log-parse-tool.js +++ b/server/tools/log-parse-tool.js @@ -27,7 +27,15 @@ const bluforPlayerCountRegex = /NATO\s(\d*)/; const opforPlayerCountRegex = /CSAT\s(\d*)/; -const timestampRegex= /LOG:\s(\d*:\d*:\d*)\s---/; +const timestampRegex = /LOG:\s(\d*:\d*:\d*)\s---/; + +const singleMinFpsRegex = /Single min\. FPS for (.*):\s(\d*.\d*)"/; // group1 = entity name, group2 = value + +const singleAvgFpsRegex = /Single avg\. FPS for (.*):\s(\d*.\d*)"/; // group1 = entity name, group2 = value + +const minFpsRegex = /Min\. FPS for (.*):\s\[(.*)\]"/; // group1 = entity name, group2 = comma separated values + +const avgFpsRegex = /Avg\. FPS for (.*):\s\[(.*)\]"/; // group1 = entity name, group2 = comma separated values const parseWarLog = (lineArray, war) => { let flagBlufor = true; @@ -48,6 +56,7 @@ const parseWarLog = (lineArray, war) => { transport: [], players: [], playerCount: [], + serverFps: [], }; const VEHICLE_BLACKLIST = [ @@ -265,6 +274,43 @@ const parseWarLog = (lineArray, war) => { countBlufor: countBlufor, countOpfor: countOpfor, }); + } else if (line.includes('(FPS)')) { + stats.clean.push(line); + + const updateServerFpsLogEntry = (entityName, addKey, addValue) => { + const idx = stats.serverFps.findIndex((entry) => entry.entityName === entityName); + if (idx === -1) { + stats.serverFps.push({ + war: war.id, + entityName: entityName, + [addKey]: addValue, + }); + return; + } + const entity = stats.serverFps[idx]; + entity[addKey] = addValue; + stats.serverFps[idx] = entity; + }; + + const singleMinMatch = singleMinFpsRegex.exec(line); + if (singleMinMatch) { + updateServerFpsLogEntry(singleMinMatch[1], 'singleMinFps', singleMinMatch[2]); + } else { + const singleAvgMatch = singleAvgFpsRegex.exec(line); + if (singleAvgMatch) { + updateServerFpsLogEntry(singleAvgMatch[1], 'singleAvgFps', singleAvgMatch[2]); + } else { + const minCollectionMatch = minFpsRegex.exec(line); + if (minCollectionMatch) { + updateServerFpsLogEntry(minCollectionMatch[1], 'minFps', minCollectionMatch[2].split(',')); + } else { + const avgCollectionMatch = avgFpsRegex.exec(line); + if (avgCollectionMatch) { + updateServerFpsLogEntry(avgCollectionMatch[1], 'avgFps', avgCollectionMatch[2].split(',')); + } + } + } + } } else if (line.includes('(Fraktionsuebersicht)') || line.includes('Fraktionsübersicht')) { /** * PLAYERS