Add parsing and persisting for FPS logs (CC-80)
							parent
							
								
									7c23c302c6
								
							
						
					
					
						commit
						03073f5436
					
				
										
											Binary file not shown.
										
									
								
							|  | @ -32,7 +32,7 @@ const LogBudgetSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logBudget', |   collection: 'logBudget', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogBudgetSchema.index({war: 1}); | LogBudgetSchema.index({war: 1}); | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ const LogFlagSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logFlag', |   collection: 'logFlag', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogFlagSchema.index({war: 1, player: 1}); | LogFlagSchema.index({war: 1, player: 1}); | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ const LogKillSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logKill', |   collection: 'logKill', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogKillSchema.index({war: 1, shooter: 1, target: 1}); | LogKillSchema.index({war: 1, shooter: 1, target: 1}); | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ const LogPlayerCountSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logPlayerCount', |   collection: 'logPlayerCount', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogPlayerCountSchema.index({war: 1}); | LogPlayerCountSchema.index({war: 1}); | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ const LogKillSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logPoints', |   collection: 'logPoints', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogKillSchema.index({war: 1, shooter: 1, target: 1}); | LogKillSchema.index({war: 1, shooter: 1, target: 1}); | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ const LogRespawnSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logRespawn', |   collection: 'logRespawn', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogRespawnSchema.index({war: 1, player: 1}); | LogRespawnSchema.index({war: 1, player: 1}); | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ const LogReviveSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logRevive', |   collection: 'logRevive', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogReviveSchema.index({war: 1, medic: 1}); | LogReviveSchema.index({war: 1, medic: 1}); | ||||||
|  |  | ||||||
|  | @ -13,6 +13,18 @@ const LogServerFpsSchema = new Schema({ | ||||||
|     type: String, |     type: String, | ||||||
|     required: true, |     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: [{ |   avgFps: [{ | ||||||
|     type: Number, |     type: Number, | ||||||
|     get: (v) => Math.round(v), |     get: (v) => Math.round(v), | ||||||
|  | @ -27,7 +39,7 @@ const LogServerFpsSchema = new Schema({ | ||||||
|   }], |   }], | ||||||
| }, { | }, { | ||||||
|   collection: 'logServerFps', |   collection: 'logServerFps', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogServerFpsSchema.index({war: 1}); | LogServerFpsSchema.index({war: 1}); | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ const LogTransportSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logTransport', |   collection: 'logTransport', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogTransportSchema.index({war: 1, driver: 1}); | LogTransportSchema.index({war: 1, driver: 1}); | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ const LogVehicleKillSchema = new Schema({ | ||||||
|   }, |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'logVehicle', |   collection: 'logVehicle', | ||||||
|   versionKey: false |   versionKey: false, | ||||||
| }); | }); | ||||||
| // optional more indices
 | // optional more indices
 | ||||||
| LogVehicleKillSchema.index({war: 1, shooter: 1, target: 1}); | LogVehicleKillSchema.index({war: 1, shooter: 1, target: 1}); | ||||||
|  |  | ||||||
|  | @ -82,6 +82,11 @@ const PlayerSchema = new Schema({ | ||||||
|     get: (v) => Math.round(v), |     get: (v) => Math.round(v), | ||||||
|     set: (v) => Math.round(v), |     set: (v) => Math.round(v), | ||||||
|   }, |   }, | ||||||
|  |   performance: { | ||||||
|  |     type: mongoose.Schema.Types.ObjectId, | ||||||
|  |     ref: 'LogServerFpsSchema', | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
| }, { | }, { | ||||||
|   collection: 'player', |   collection: 'player', | ||||||
|   timestamps: {createdAt: 'timestamp'}, |   timestamps: {createdAt: 'timestamp'}, | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ const LogFlagModel = require('../models/logs/flag'); | ||||||
| const LogBudgetModel = require('../models/logs/budget'); | const LogBudgetModel = require('../models/logs/budget'); | ||||||
| const LogPointsModel = require('../models/logs/points'); | const LogPointsModel = require('../models/logs/points'); | ||||||
| const LogPlayerCountModel = require('../models/logs/player-count'); | const LogPlayerCountModel = require('../models/logs/player-count'); | ||||||
|  | const LogServerFpsModel = require('../models/logs/server-fps'); | ||||||
| 
 | 
 | ||||||
| // util
 | // util
 | ||||||
| const genericPatch = require('./_generic').genericPatch; | const genericPatch = require('./_generic').genericPatch; | ||||||
|  | @ -59,25 +60,20 @@ wars.route('/') | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|     .post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => { |     .post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => { | ||||||
|       const body = req.body; |         const body = req.body; | ||||||
|       const warBody = new WarModel(body); |         const warBody = new WarModel(body); | ||||||
| 
 | 
 | ||||||
|       if (req.file) { |         if (req.file) { | ||||||
|         fs.readFile(req.file.buffer, (file, err) => { |           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 (err) { |             if (err) { | ||||||
|               return next(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) { |               if (err) { | ||||||
|                 return next(err); |                 return next(err); | ||||||
|               } |               } | ||||||
| 
 |  | ||||||
|               LogKillModel.create(statsResult.kills, () => { |               LogKillModel.create(statsResult.kills, () => { | ||||||
|                 LogVehicleKillModel.create(statsResult.vehicles, () => { |                 LogVehicleKillModel.create(statsResult.vehicles, () => { | ||||||
|                   LogRespawnModel.create(statsResult.respawn, () => { |                   LogRespawnModel.create(statsResult.respawn, () => { | ||||||
|  | @ -87,27 +83,43 @@ wars.route('/') | ||||||
|                           LogTransportModel.create(statsResult.transport, () => { |                           LogTransportModel.create(statsResult.transport, () => { | ||||||
|                             LogPlayerCountModel.create(statsResult.playerCount, () => { |                             LogPlayerCountModel.create(statsResult.playerCount, () => { | ||||||
|                               LogPointsModel.create(statsResult.points, () => { |                               LogPointsModel.create(statsResult.points, () => { | ||||||
|                                 const folderName = resourceLocation.concat(war._id); |                                 LogServerFpsModel.create(statsResult.serverFps, (err, serverPerformanceEntries) => { | ||||||
|                                 mkdirp(folderName, (err) => { |                                   serverPerformanceEntries.forEach((entry) => { | ||||||
|                                   if (err) return next(err); |                                     const idx = statsResult.players | ||||||
| 
 |                                                            .findIndex((player) => player.name === entry.entityName); | ||||||
|                                   // save clean log file
 |                                     if (idx !== -1) { | ||||||
|                                   const cleanFile = fs.createWriteStream(folderName + '/clean.log'); |                                       const player = statsResult.players[idx]; | ||||||
|                                   statsResult.clean.forEach((cleanLine) => { |                                       player['performance'] = entry._id; | ||||||
|                                     cleanFile.write(cleanLine + '\n\n'); |                                       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
 |                                       // save clean log file
 | ||||||
|                                   const rawFile = fs.createWriteStream(folderName + '/war.log'); |                                       const cleanFile = fs.createWriteStream(folderName + '/clean.log'); | ||||||
|                                   lineArray.forEach((rawLine) => { |                                       statsResult.clean.forEach((cleanLine) => { | ||||||
|                                     rawFile.write(rawLine + '\n'); |                                         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 { | ||||||
|       } else { |           const err = new Error('no Logfile provided'); | ||||||
|         const err = new Error('no Logfile provided'); |           err.status = codes.wrongmediasend; | ||||||
|         err.status = codes.wrongmediasend; |           next(err); | ||||||
|         next(err); |         } | ||||||
|       } |       } | ||||||
|     }) |     ) | ||||||
| 
 | 
 | ||||||
|     .all( |     .all( | ||||||
|       routerHandling.httpMethodNotAllowed |       routerHandling.httpMethodNotAllowed | ||||||
|  |  | ||||||
|  | @ -27,7 +27,15 @@ const bluforPlayerCountRegex = /NATO\s(\d*)/; | ||||||
| 
 | 
 | ||||||
| const opforPlayerCountRegex = /CSAT\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) => { | const parseWarLog = (lineArray, war) => { | ||||||
|   let flagBlufor = true; |   let flagBlufor = true; | ||||||
|  | @ -48,6 +56,7 @@ const parseWarLog = (lineArray, war) => { | ||||||
|     transport: [], |     transport: [], | ||||||
|     players: [], |     players: [], | ||||||
|     playerCount: [], |     playerCount: [], | ||||||
|  |     serverFps: [], | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const VEHICLE_BLACKLIST = [ |   const VEHICLE_BLACKLIST = [ | ||||||
|  | @ -265,6 +274,43 @@ const parseWarLog = (lineArray, war) => { | ||||||
|         countBlufor: countBlufor, |         countBlufor: countBlufor, | ||||||
|         countOpfor: countOpfor, |         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')) { |     } else if (line.includes('(Fraktionsuebersicht)') || line.includes('Fraktionsübersicht')) { | ||||||
|       /** |       /** | ||||||
|        * PLAYERS |        * PLAYERS | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue