Skip to content

Commit 0d3ecc3

Browse files
authored
feature: fix up demo parsing , system logging (#309)
1 parent dab47e4 commit 0d3ecc3

5 files changed

Lines changed: 102 additions & 10 deletions

File tree

components/match/MatchAdminBottomBar.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ const canSendRCONCommands = computed(
181181
].includes(props.match.status) && !!props.match.server_id,
182182
);
183183
184+
const canShowLogs = computed(
185+
() =>
186+
![
187+
e_match_status_enum.Scheduled,
188+
e_match_status_enum.WaitingForCheckIn,
189+
e_match_status_enum.PickingPlayers,
190+
e_match_status_enum.Veto,
191+
].includes(props.match.status),
192+
);
193+
184194
const restorableRounds = computed(() =>
185195
(currentMap.value?.rounds ?? []).filter(
186196
(r: any) => r.has_backup_file && r.round > 0,
@@ -386,7 +396,7 @@ function runCommand(
386396
class="min-w-0 flex flex-col gap-2 lg:min-h-0 lg:overflow-hidden"
387397
>
388398
<div
389-
v-if="!hasLogs"
399+
v-if="!canShowLogs || !hasLogs"
390400
class="flex flex-col items-center justify-center gap-2 rounded-md border border-dashed border-border p-6 text-center"
391401
>
392402
<h3 class="font-semibold">
@@ -397,6 +407,7 @@ function runCommand(
397407
</p>
398408
</div>
399409
<ServiceLogs
410+
v-if="canShowLogs"
400411
v-show="hasLogs"
401412
:service="`m-${match.id}`"
402413
:compact="true"

components/match/MatchMaps.vue

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import {
77
ChevronRight,
88
ChevronLeft,
99
PlayCircle,
10+
RefreshCw,
11+
Loader2,
1012
} from "lucide-vue-next";
13+
import { toast } from "@/components/ui/toast";
14+
import { generateMutation } from "~/graphql/graphqlGen";
1115
import {
1216
Tooltip,
1317
TooltipContent,
@@ -71,19 +75,49 @@ import cleanMapName from "~/utilities/cleanMapName";
7175
class="text-xs px-2 py-0.5 backdrop-blur-sm"
7276
>{{ $t("match.decider") }}</Badge
7377
>
74-
<Tooltip v-if="hasDemo">
78+
<Tooltip v-if="hasDemo && canWatchDemo && hasDemoMetadata">
7579
<TooltipTrigger as-child>
7680
<Button
7781
size="xs"
7882
variant="ghost"
7983
class="h-6 w-6 p-0 text-white/70 hover:text-white"
80-
@click.stop="openDemoWatcher"
84+
@click.stop="openDemoWatcher()"
8185
>
8286
<PlayCircle class="w-4 h-4" />
8387
</Button>
8488
</TooltipTrigger>
8589
<TooltipContent>Watch demo</TooltipContent>
8690
</Tooltip>
91+
<Tooltip v-else-if="hasDemo && canParseDemo">
92+
<TooltipTrigger as-child>
93+
<Button
94+
size="xs"
95+
variant="ghost"
96+
class="h-6 w-6 p-0 text-white/70 hover:text-white disabled:opacity-50 disabled:cursor-not-allowed"
97+
:disabled="isParsingDemo"
98+
@click.stop="parseDemo()"
99+
>
100+
<Loader2 v-if="isParsingDemo" class="w-4 h-4 animate-spin" />
101+
<RefreshCw v-else class="w-4 h-4" />
102+
</Button>
103+
</TooltipTrigger>
104+
<TooltipContent>
105+
{{ isParsingDemo ? "Parsing demo…" : "Parse demo metadata" }}
106+
</TooltipContent>
107+
</Tooltip>
108+
<Tooltip v-else-if="hasDemo && canWatchDemo">
109+
<TooltipTrigger as-child>
110+
<Button
111+
size="xs"
112+
variant="ghost"
113+
class="h-6 w-6 p-0 text-white/70 opacity-50 cursor-not-allowed"
114+
disabled
115+
>
116+
<PlayCircle class="w-4 h-4" />
117+
</Button>
118+
</TooltipTrigger>
119+
<TooltipContent>Demo metadata has not been parsed</TooltipContent>
120+
</Tooltip>
87121
<template v-if="matchMap.demos_download_url">
88122
<a target="_blank" :href="matchMap.demos_download_url" @click.stop>
89123
<Button
@@ -203,7 +237,12 @@ import cleanMapName from "~/utilities/cleanMapName";
203237
</template>
204238

205239
<script lang="ts">
206-
import { e_match_status_enum, e_veto_pick_types_enum } from "~/generated/zeus";
240+
import {
241+
e_match_status_enum,
242+
e_player_roles_enum,
243+
e_veto_pick_types_enum,
244+
} from "~/generated/zeus";
245+
import { useAuthStore } from "~/stores/AuthStore";
207246
208247
export default {
209248
emits: ["open-stats"],
@@ -221,6 +260,11 @@ export default {
221260
default: false,
222261
},
223262
},
263+
data() {
264+
return {
265+
isParsingDemo: false,
266+
};
267+
},
224268
computed: {
225269
canOpenStats() {
226270
if (this.matchMap.status === e_match_status_enum.Scheduled) {
@@ -229,14 +273,25 @@ export default {
229273
return (this.match.options?.best_of ?? 1) > 1;
230274
},
231275
hasDemo() {
232-
// Either the per-map aggregate (demos_total_size > 0) or the
233-
// demos relation has at least one row. Mirrors the v-if guards
234-
// already used by the download button below.
235276
return (
236277
!!this.matchMap.demos_total_size ||
237278
(this.matchMap.demos?.length ?? 0) > 0
238279
);
239280
},
281+
canWatchDemo() {
282+
return (
283+
this.match.is_organizer ||
284+
useAuthStore().isRoleAbove(e_player_roles_enum.streamer)
285+
);
286+
},
287+
hasDemoMetadata() {
288+
return (this.matchMap.demos ?? []).some(
289+
(d) => !!d.metadata_parsed_at && !!d.total_ticks,
290+
);
291+
},
292+
canParseDemo() {
293+
return useAuthStore().isAdmin;
294+
},
240295
showTeamPatch() {
241296
return (
242297
!this.isDecider || this.matchMap.status === e_match_status_enum.Live
@@ -268,6 +323,29 @@ export default {
268323
},
269324
},
270325
methods: {
326+
async parseDemo() {
327+
if (this.isParsingDemo) return;
328+
this.isParsingDemo = true;
329+
try {
330+
await this.$apollo.mutate({
331+
mutation: generateMutation({
332+
reparseDemo: [
333+
{ match_map_id: this.matchMap.id },
334+
{ success: true },
335+
],
336+
}),
337+
});
338+
toast({ title: "Demo parsed" });
339+
} catch (error) {
340+
toast({
341+
title: "Failed to parse demo",
342+
description: (error as Error)?.message,
343+
variant: "destructive",
344+
});
345+
} finally {
346+
this.isParsingDemo = false;
347+
}
348+
},
271349
openDemoWatcher() {
272350
// Pop the demo watcher into a dedicated window. The popup owns
273351
// the session lifecycle: closing the window kills the session

components/match/PlayerStatusDisplay.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
e_match_status_enum,
77
} from "~/generated/zeus";
88
import { useSidebar } from "../ui/sidebar";
9+
10+
const { isMobile } = useSidebar();
911
</script>
1012

1113
<template>
@@ -155,9 +157,6 @@ export default {
155157
},
156158
},
157159
computed: {
158-
isMobile() {
159-
return useSidebar().isMobile.value;
160-
},
161160
e_match_status_enum() {
162161
return e_match_status_enum;
163162
},

pages/matches/[id].vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ export default {
488488
demos: {
489489
size: true,
490490
download_url: true,
491+
metadata_parsed_at: true,
492+
total_ticks: true,
491493
},
492494
rounds: [
493495
{

pages/system-logs.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ const SYSTEM_LOG_SERVICES = [
1414
"api",
1515
"web",
1616
"game-server-node",
17+
"demo-parser",
1718
"hasura",
1819
"typesense",
1920
"timescaledb",
2021
"redis",
2122
"minio",
23+
"mediamtx",
2224
];
2325
2426
const services = SYSTEM_LOG_SERVICES;

0 commit comments

Comments
 (0)