monitor/scripts/dev/makeOldStatsFile.code.ts
2025-04-16 22:30:27 +07:00

131 lines
4.6 KiB
TypeScript

/**
* This script is used to merge multiple stats_playerDrop files into a single file with the last 336 hours of data.
* NOTE: use npx tsx scripts/dev/makeOldStatsFile.local.ts to run this script
*/
import fs from 'node:fs';
import { shuffle } from 'd3-array';
import { PDLChangeEventType, PDLFileType } from '../../core/modules/Metrics/playerDrop/playerDropSchemas';
import { MultipleCounter } from '../../core/modules/Metrics/statsUtils';
type CountersType = {
drops: MultipleCounter,
resKicks: MultipleCounter,
crashes: MultipleCounter,
changes: PDLChangeEventType[],
};
const getRandomResource = () => {
const pool = [
'anticheat', 'anticheat', 'anticheat', 'anticheat',
'txAdmin', 'txAdmin', 'txAdmin',
'vMenu',
'es_extended',
];
return pool[Math.floor(Math.random() * pool.length)];
}
export default (sourceFiles: string[], targetFileName: string) => {
let logCounters: CountersType[] = [];
for (const fileName of sourceFiles) {
const fileData = JSON.parse(fs.readFileSync(fileName, 'utf8')) as PDLFileType;
console.log('fileData.log.length:', fileData.log.length);
for (const log of fileData.log) {
const resKicks = new MultipleCounter();
const drops = log.dropTypes.map(([type, count]): [string, number] | false => {
if (type === 'user-initiated') {
return ['player', count]
} else if (type === 'server-initiated') {
resKicks.count('txAdmin', count);
return ['resource', count];
} else if (type === 'unknown') {
if (Math.random() <= 0.2) {
return [type, count];
} else {
resKicks.count(getRandomResource(), count);
return ['resource', count];
}
} else {
return [type, count];
}
}).filter((x): x is [string, number] => Array.isArray(x));
logCounters.push({
// drops: new MultipleCounter(log.dropTypes.filter(([type]) => type !== 'server-initiated')),
drops: new MultipleCounter(drops),
resKicks: resKicks,
crashes: new MultipleCounter(log.crashTypes),
changes: log.changes,
});
}
}
console.log('Logs Hours:', logCounters.length);
//Shuffle and merge all crashes into 336 MultipleCounters and shuffle them again
logCounters = shuffle(logCounters);
let mergedCounters: CountersType[] = [];
for (let i = 0; i < logCounters.length; i++) {
const writeIndex = i % 336;
const currCounters = logCounters[i];
if (!mergedCounters[writeIndex]) {
mergedCounters[writeIndex] = currCounters;
} else {
mergedCounters[writeIndex].drops.merge(currCounters.drops);
mergedCounters[writeIndex].resKicks.merge(currCounters.resKicks);
mergedCounters[writeIndex].crashes.merge(currCounters.crashes);
mergedCounters[writeIndex].changes.push(...currCounters.changes);
}
}
mergedCounters = shuffle(mergedCounters);
console.log('mergedCrashes.length:', mergedCounters.length);
//Prepare new hours array
const last336Hours: Date[] = [];
const currentDate = new Date();
currentDate.setMinutes(0, 0, 0);
for (let i = 335; i >= 0; i--) {
const hour = new Date(currentDate.getTime() - i * 60 * 60 * 1000);
last336Hours.push(hour);
}
console.log('last336Hours.length:', last336Hours.length);
//Prepare new log with the data
const newLog: PDLFileType['log'] = [];
for (const hour of last336Hours) {
const counters = mergedCounters.pop()!;
const newHour = {
hour: hour.toISOString(),
changes: counters?.changes ?? [],
dropTypes: counters?.drops.toArray() ?? [],
resKicks: counters?.resKicks.toArray() ?? [],
crashTypes: counters?.crashes.toArray() ?? [],
};
newLog.push(newHour);
console.dir({
hour: newHour.hour,
changes: newHour.changes.length,
drops: counters.drops.sum(),
resKicks: counters.resKicks.sum(),
crashes: counters.crashes.sum(),
});
}
//Write new file
const newStatsFile: PDLFileType = {
version: 2,
lastGameVersion: 'unknown',
lastServerVersion: 'unknown',
lastResourceList: [],
lastUnknownReasons: [],
log: newLog,
}
fs.writeFileSync(targetFileName, JSON.stringify(newStatsFile));
}