Skip to content

Commit

Permalink
chore: version bump + added player crashes page
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed Jun 11, 2024
1 parent 7d4494f commit 3e86661
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 1 deletion.
15 changes: 15 additions & 0 deletions core/components/StatsManager/playerDrop/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ export default class PlayerDropStatsManager {
}


/**
* Get the recent category count for player drops in the last X hours
*/
public getRecentCrashes(windowHours: number) {
const logCutoff = (new Date).setUTCMinutes(0, 0, 0) - (windowHours * 60 * 60 * 1000) - 1;
const flatCounts = this.eventLog
.filter((entry) => entry.hour.dateHourTs >= logCutoff)
.map((entry) => entry.crashTypes.toSortedValuesArray())
.flat();
const cumulativeCounter = new MultipleCounter();
cumulativeCounter.merge(flatCounts);
return cumulativeCounter.toSortedValuesArray();
}


/**
* Returns the object of the current hour object in log.
* Creates one if doesn't exist one for the current hour.
Expand Down
1 change: 1 addition & 0 deletions core/components/WebServer/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export default (config: WebServerConfigType) => {
router.get('/serverLog/partial', apiAuthMw, webRoutes.serverLogPartial);
router.get('/systemLog/:scope', apiAuthMw, webRoutes.systemLogs);
router.get('/perfChartData/:thread', apiAuthMw, webRoutes.perfChart);
router.get('/playerCrashesData', apiAuthMw, webRoutes.playerCrashes);

/*
FIXME: reorganizar TODAS rotas de logs, incluindo listagem e download
Expand Down
1 change: 1 addition & 0 deletions core/webroutes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { default as diagnostics_sendReport } from './diagnostics/sendReport';
export { default as intercom } from './intercom.js';
export { default as resources } from './resources.js';
export { default as perfChart } from './perfChart';
export { default as playerCrashes } from './playerCrashes.js';
export { default as systemLogs } from './systemLogs';

export { default as auth_addMasterPin } from './authentication/addMasterPin.js';
Expand Down
25 changes: 25 additions & 0 deletions core/webroutes/playerCrashes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const modulename = 'WebServer:PerfChart';
import { AuthedCtx } from '@core/components/WebServer/ctxTypes';
import consoleFactory from '@extras/console';
import { DeepReadonly } from 'utility-types';
const console = consoleFactory(modulename);


//Types
export type PlayerCrashesApiErrorResp = {
fail_reason: string;
};
export type PlayerCrashesApiSuccessResp = [reason: string, count: number][];
export type PlayerCrashesApiResp = DeepReadonly<PlayerCrashesApiErrorResp | PlayerCrashesApiSuccessResp>;


/**
* Returns the data required to build the dashboard performance chart of a specific thread
*/
export default async function perfChart(ctx: AuthedCtx) {
const sendTypedResp = (data: PlayerCrashesApiResp) => ctx.send(data);

const crashSummary = ctx.txAdmin.statsManager.playerDrop.getRecentCrashes(24);

return sendTypedResp(crashSummary);
};
2 changes: 1 addition & 1 deletion fxmanifest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
author 'Tabarra'
description 'The official FiveM/RedM server web/in-game management platform.'
repository 'https://github.com/tabarra/txAdmin'
version '7.2.0'
version '7.2.2'
ui_label 'txAdmin'

rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
Expand Down
6 changes: 6 additions & 0 deletions panel/src/layout/MainRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import BanTemplatesPage from "@/pages/BanTemplates/BanTemplatesPage";
import SystemLogPage from "@/pages/SystemLogPage";
import AddLegacyBanPage from "@/pages/AddLegacyBanPage";
import DashboardPage from "@/pages/Dashboard/DashboardPage";
import PlayerCrashesPage from "@/pages/PlayerCrashesPage";


type RouteType = {
Expand Down Expand Up @@ -124,6 +125,11 @@ const allRoutes: RouteType[] = [
title: 'Ban Identifiers',
children: <AddLegacyBanPage />
},
{
path: '/player-crashes',
title: 'Player Crashes',
children: <PlayerCrashesPage />
},
];


Expand Down
85 changes: 85 additions & 0 deletions panel/src/pages/PlayerCrashesPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import InlineCode from "@/components/InlineCode";
import { DndSortableGroup, DndSortableItem } from "@/components/dndSortable";
import { useBackendApi } from "@/hooks/fetch";
import { Loader2Icon } from "lucide-react";
import { PlayerCrashesApiResp } from "@shared/otherTypes";
import useSWR from "swr";
import { useState } from "react";

function CrashReasonCard({ reason, count, totalCrashes }: { reason: string, count: number, totalCrashes: number }) {
const percentage = ((count / totalCrashes) * 100).toFixed(2);

return (
<div className="bg-card rounded-lg border px-2 py-3 flex gap-3 relative">
<div className="flex-shrink-0 rounded-md px-3 py-1 m-auto text-sm font-medium bg-secondary text-secondary-foreground">
<span>{count}</span>
<span className="ml-1 ">({percentage}%)</span>
</div>
<p className="flex-1 text-sm ">
{reason} lipsu
</p>
</div>
)
}


export default function PlayerCrashesPage() {
const playerCrashesApi = useBackendApi<PlayerCrashesApiResp>({
method: 'GET',
path: `/playerCrashesData`,
});

const swrDataApiResp = useSWR('/playerCrashesData', async () => {
const data = await playerCrashesApi({});
if (!data) throw new Error('empty_response');
if ('fail_reason' in data) {
throw new Error(data.fail_reason);
}
return data as [reason: string, count: number][];
}, {});

let totalCrashes = 0;
if (swrDataApiResp.data) {
console.log('asdfsfdgfsd');
totalCrashes = swrDataApiResp.data?.reduce((acc, [, count]) => acc + count, 0) ?? 0
}
return <>
<div className="space-y-4 w-full max-w-screen-lg mx-auto">
<div className="px-2 md:px-0">
<h1 className="text-3xl mb-2">Crash Reasons - last 24h</h1>
<p>
Here you can see all the player crash reasons in the last 24 hours. <br />
This is useful for troubleshooting and solving bugs. <br />
<span className="text-muted-foreground italic">
NOTE: Some crash reasons might be missing due to players using translated game clients.<br />
</span>
</p>
</div>
<div className="space-y-2">
<div className="flex flex-wrap justify-between text-muted-foreground px-2 md:px-0">
<span className="shrink-0">Reasons: {swrDataApiResp.data?.length ?? 0}</span>
{swrDataApiResp.error ? (
<div className="inline-flex flex-wrap gap-1">
<span className='text-destructive-inline'>Error loading: {swrDataApiResp.error.message}</span>
</div>
) : swrDataApiResp.isLoading || swrDataApiResp.isValidating ? (
<span className="text-muted-foreground">
Loading...
</span>
) : null}
</div>

<div className="space-y-4">
{swrDataApiResp.data?.map(([reason, count]) => (
<CrashReasonCard
key={reason}
reason={reason}
count={count}
totalCrashes={totalCrashes}
/>
))}
</div>
</div>
</div>
</>;
}
1 change: 1 addition & 0 deletions shared/otherTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type { ApiAddLegacyBanReqSchema, ApiRevokeActionReqSchema } from "@core/w
export type { SvRtLogFilteredType, SvRtPerfCountsThreadType } from "@core/components/StatsManager/svRuntime/perfSchemas";
export type { SvRtPerfThreadNamesType } from "@core/components/StatsManager/svRuntime/config";
export type { PerfChartApiResp, PerfChartApiSuccessResp } from "@core/webroutes/perfChart";
export type { PlayerCrashesApiResp, PlayerCrashesApiSuccessResp } from "@core/webroutes/playerCrashes";


export type UpdateDataType = {
Expand Down

0 comments on commit 3e86661

Please sign in to comment.