Skip to content

Commit

Permalink
wip(perf): playerDrop tested and working
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed May 27, 2024
1 parent def627d commit 457c68f
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 35 deletions.
8 changes: 7 additions & 1 deletion core/components/Logger/handlers/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ export default class ServerLogger extends LoggerBase {

} else if (eventData.type === 'LoggerStarted') {
eventMessage = 'Logger started';
globals?.statsManager.playerDrop.handleServerBootData(eventData.data);
if (typeof eventData.data?.projectName === 'string' && eventData.data.projectName.length) {
globals?.persistentCache.set('fxsRuntime:projectName', eventData.data.projectName);
}

} else if (eventData.type === 'DebugMessage') {
eventMessage = (typeof eventData.data === 'string')
Expand All @@ -215,7 +219,7 @@ export default class ServerLogger extends LoggerBase {
? `${eventData.data.message}`
: 'did unknown action';

} else if (eventData.type !== 'playerJoining') {
} else {
console.verbose.warn(`Unrecognized event: ${eventData.type}`);
console.verbose.dir(eventData);
eventMessage = eventData.type;
Expand Down Expand Up @@ -243,6 +247,7 @@ export default class ServerLogger extends LoggerBase {
* @param {Number} sliceLength
*/
readPartialNewer(timestamp, sliceLength) {
//FIXME: use d3 bissect to optimize this
const limitIndex = this.recentBuffer.findIndex((x) => x.ts > timestamp);
return this.recentBuffer.slice(limitIndex, limitIndex + sliceLength);
}
Expand All @@ -254,6 +259,7 @@ export default class ServerLogger extends LoggerBase {
* @param {Number} sliceLength
*/
readPartialOlder(timestamp, sliceLength) {
//FIXME: use d3 bissect to optimize this
const limitIndex = this.recentBuffer.findIndex((x) => x.ts >= timestamp);

if (limitIndex === -1) {
Expand Down
1 change: 1 addition & 0 deletions core/components/PlayerlistManager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export default class PlayerlistManager {
if (!(this.#playerlist[payload.id] instanceof ServerPlayer)) throw new Error(`player id not found`);
this.#playerlist[payload.id]!.disconnect();
this.joinLeaveLog.push([currTs, false]);
this.#txAdmin.statsManager.playerDrop.handlePlayerDrop(payload.reason);
this.#txAdmin.logger.server.write([{
type: 'playerDropped',
src: payload.id,
Expand Down
2 changes: 1 addition & 1 deletion core/components/StatsManager/playerDrop/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
const daysMs = 24 * 60 * 60 * 1000;
export const PDL_CRASH_REASON_CHAR_LIMIT = 512;
export const PDL_UNKNOWN_REASON_CHAR_LIMIT = 320;
export const PDL_UNKNOWN_LIST_SIZE_LIMIT = 200;
export const PDL_UNKNOWN_LIST_SIZE_LIMIT = 200; //at most 62.5kb (200*320/1024)
export const PDL_RETENTION = 14 * daysMs;
60 changes: 35 additions & 25 deletions core/components/StatsManager/playerDrop/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export default class PlayerDropStatsManager {
dateHourStr: dateHourStr,
},
changes: [],
crashes: [],
dropCounts: new MultipleCounter(),
crashTypes: new MultipleCounter(),
dropTypes: new MultipleCounter(),
};
this.eventLog.push(newHourLog);
return newHourLog;
Expand All @@ -66,7 +66,7 @@ export default class PlayerDropStatsManager {
*/
public handleServerBootData(rawPayload: any) {
const logRef = this.getCurrentLogHourRef();

//Parsing data
const validation = PDLServerBootDataSchema.safeParse(rawPayload);
if (!validation.success) {
Expand All @@ -78,39 +78,41 @@ export default class PlayerDropStatsManager {

//Game version change
const gameString = `${gameName}:${gameBuild}`;
if (gameString !== this.lastGameVersion) {
shouldSave = true;
if (gameString) {
if (!this.lastGameVersion) {
shouldSave = true;
} else if (gameString !== this.lastGameVersion) {
shouldSave = true;
logRef.changes.push({
ts: Date.now(),
type: 'gameChanged',
newVersion: gameString,
});
}
this.lastGameVersion = gameString;
logRef.changes.push({
ts: Date.now(),
type: 'gameChanged',
newVersion: gameString,
});
}

//Server version change
let { build: serverBuild, platform: serverPlatform } = parseFxserverVersion(fxsVersion);
const fxsVersionString = `${serverPlatform}:${serverBuild}`;
if (fxsVersionString !== this.lastServerVersion) {
shouldSave = true;
if (fxsVersionString) {
if (!this.lastServerVersion) {
shouldSave = true;
} else if (fxsVersionString !== this.lastServerVersion) {
shouldSave = true;
logRef.changes.push({
ts: Date.now(),
type: 'fxsChanged',
newVersion: fxsVersionString,
});
}
this.lastServerVersion = fxsVersionString;
logRef.changes.push({
ts: Date.now(),
type: 'fxsChanged',
newVersion: fxsVersionString,
});
}

//Resource list change - if no resources, ignore as that's impossible
if (resources.length) {
if (!this.lastResourceList || !this.lastResourceList.length) {
shouldSave = true;
logRef.changes.push({
ts: Date.now(),
type: 'resourcesChanged',
resAdded: resources,
resRemoved: [],
});
} else {
const resAdded = resources.filter(r => !this.lastResourceList!.includes(r));
const resRemoved = this.lastResourceList.filter(r => !resources.includes(r));
Expand All @@ -124,6 +126,7 @@ export default class PlayerDropStatsManager {
});
}
}
this.lastResourceList = resources;
}

//Saving if needed
Expand All @@ -144,7 +147,9 @@ export default class PlayerDropStatsManager {
if (category === 'crash') {
logRef.crashTypes.count(cleanReason);
} else if (category === 'unknown') {
this.lastUnknownReasons.push(cleanReason);
if (!this.lastUnknownReasons.includes(cleanReason)) {
this.lastUnknownReasons.push(cleanReason);
}
}
}
this.saveEventLog();
Expand Down Expand Up @@ -189,10 +194,15 @@ export default class PlayerDropStatsManager {
console.verbose.debug(`Loaded ${this.eventLog.length} log entries from cache`);
this.optimizeStatsLog();
} catch (error) {
if (error instanceof ZodError) {
if ((error as any)?.code === 'ENOENT') {
console.verbose.debug(`${LOG_DATA_FILE_NAME} not found, starting with empty stats.`);
this.resetLog('File was just created, no data yet');
} else if (error instanceof ZodError) {
console.warn(`Failed to load ${LOG_DATA_FILE_NAME} due to invalid data.`);
this.resetLog('Failed to load log file due to invalid data');
} else {
console.warn(`Failed to load ${LOG_DATA_FILE_NAME} with message: ${(error as Error).message}`);
this.resetLog('Failed to load log file due to unknown error');
}
console.warn('Since this is not a critical file, it will be reset.');
}
Expand Down
4 changes: 3 additions & 1 deletion core/components/StatsManager/svRuntime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ export default class SvRuntimeStatsManager {
console.verbose.debug(`Loaded ${this.statsLog.length} performance snapshots from cache`);
await optimizeSvRuntimeLog(this.statsLog);
} catch (error) {
if (error instanceof ZodError) {
if ((error as any)?.code === 'ENOENT') {
console.verbose.debug(`${LOG_DATA_FILE_NAME} not found, starting with empty stats.`);
} else if (error instanceof ZodError) {
console.warn(`Failed to load ${LOG_DATA_FILE_NAME} due to invalid data.`);
} else {
console.warn(`Failed to load ${LOG_DATA_FILE_NAME} with message: ${(error as Error).message}`);
Expand Down
2 changes: 1 addition & 1 deletion core/components/StatsManager/txRuntime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const console = consoleFactory(modulename);


//Consts
const JWE_VERSION = 1;
const JWE_VERSION = 12;
const statsPublicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2NCbB5DvpR7F8qHF9SyA
xJKv9lpGO2PiU5wYUmEQaa0IUrUZmQ8ivsoOyCZOGKN9PESsVyqZPx37fhtAIqNo
Expand Down
1 change: 1 addition & 0 deletions core/webroutes/chartData.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const caches = {
svMain: new Cache(30),
};

//NOTE: used in sendReport.ts
export const getChartData = (threadName) => {
//If cache available
const cachedData = caches[threadName].get();
Expand Down
1 change: 1 addition & 0 deletions core/webroutes/deployer/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ async function handleSaveConfig(ctx) {
}

globals.fxRunner.refreshConfig();
globals.statsManager.playerDrop.resetLog('Server Data Path or CFG Path changed.');
ctx.admin.logAction('Completed and committed server deploy.');

//Starting server
Expand Down
5 changes: 5 additions & 0 deletions core/webroutes/settings/save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ async function handleFXServer(ctx: AuthedCtx) {

//Preparing & saving config
const newConfig = ctx.txAdmin.configVault.getScopedStructure('fxRunner');
const hasServerDataPathChanged = (newConfig.serverDataPath !== cfg.serverDataPath);
newConfig.serverDataPath = cfg.serverDataPath;
const hasCfgPathChanged = (newConfig.cfgPath !== cfg.cfgPath);
newConfig.cfgPath = cfg.cfgPath;
newConfig.onesync = cfg.onesync;
newConfig.autostart = cfg.autostart;
Expand All @@ -181,6 +183,9 @@ async function handleFXServer(ctx: AuthedCtx) {
}

//Sending output
if(hasServerDataPathChanged || hasCfgPathChanged){
ctx.txAdmin.statsManager.playerDrop.resetLog('Server Data Path or CFG Path changed.');
}
ctx.txAdmin.fxRunner.refreshConfig();
ctx.admin.logAction('Changing fxRunner settings.');
return ctx.send({
Expand Down
1 change: 1 addition & 0 deletions core/webroutes/setup/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ async function handleSaveLocal(ctx) {
try {
globals.configVault.saveProfile('global', newGlobalConfig);
globals.configVault.saveProfile('fxRunner', newFXRunnerConfig);
globals.statsManager.playerDrop.resetLog('Server Data Path or CFG Path changed.');
} catch (error) {
console.warn(`[${ctx.admin.name}] Error changing global/fxserver settings via setup stepper.`);
console.verbose.dir(error);
Expand Down
31 changes: 26 additions & 5 deletions docs/dev_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@
- [x] added filters to the in-game playerlist
- New Statistics stuff:
- [x] reorganize stats into a unified statsManager component
- [x] check HbData
- [ ] add simple player drop stats
- [x] player drop classifier
- [x] optimize log for big servers
- [ ] updated: fxserver
- [ ] updated: game build
- [ ] changed resources
- [ ] changed server data should reset the log (setup, deployer, settings)
- [ ] decide on a max sizes for the log
- [x] updated: fxserver
- [x] updated: game build
- [x] changed resources
- [x] changed server data should reset the log (setup, deployer, settings)
- [x] decide on a max sizes for the log
- [ ] write code to visualize the data
- [ ] write txRuntime log optimizer
- [ ] NEW PAGE: Dashboard
Expand Down Expand Up @@ -75,6 +76,26 @@
- [ ] MUST `//FIXME: update_txdiagnostics`


==================================================

d3.timeHours(new Date(1715741829000), new Date())[0]



Initial snap tem que esperar 5 mins do uptime do boot do sv, assim garantimos que cada snap tem pelo menos 5 mins
Fazer resolução ser 3 consts ao invés de array
Pra conseguir fazer otimização continuas, não é possível combinar um snap 2x com um snap 1x, então fazer múltiplos
5, 15, 30mins


let toCombine = [];
for log in statsLog:
- log.ts - toCombine[0].ts < resolution
- toCombine.push(log)
- else:
- combined = combine(toCombine)
- splice a array


## Next up... ish
- [ ] add average session time tracking to statsManager.playerDrop
Expand Down
30 changes: 29 additions & 1 deletion resource/sv_logger.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,35 @@ CreateThread(function()
end
end
end)
logger('tx', 'LoggerStarted')

--Send initial data
CreateThread(function()
local resList = {}
local resCount = GetNumResources() - 1
for i = 0, resCount do
local resName = GetResourceByFindIndex(i)
if GetResourceState(resName) == 'started' then
local resVersion = GetResourceMetadata(resName, 'version')
if type(resVersion) == 'string' and #resVersion > 0 then
resList[#resList+1] = resName..'/'..resVersion
else
resList[#resList+1] = resName
end
end
end

logger('tx', 'LoggerStarted', {
--txAdmin.statsManager.playerDrops data
gameName = GetConvar('gamename', 'gta5'),
gameBuild = GetConvar('sv_enforceGameBuild', 'invalid'),
fxsVersion = GetConvar('version', 'invalid'),
resources = resList,

--not being used anywhere for now
projectName = GetConvar('sv_projectName', 'invalid')
--NOTE: unmfortunately its not possible to retrieve the server icon
})
end)


-- Explosion handler
Expand Down

0 comments on commit 457c68f

Please sign in to comment.