🚧 Changed GameState to make it dynamic with svelte components and cleaned code

This commit is contained in:
Proxyfil
2026-04-22 23:29:25 +02:00
parent e612382eed
commit 749fb72d5d
5 changed files with 139 additions and 181 deletions
+30 -71
View File
@@ -5,10 +5,7 @@ SPDX-License-Identifier: MPL-2.0
-->
<script lang="ts">
import type { QuizData } from '$lib/quiz_types';
import { getLocalization } from '$lib/i18n';
import { get_question_title } from '$lib/admin.ts';
import type { PlayerAnswer } from '$lib/admin.ts';
import { socket } from './socket';
import { QuizQuestionType } from '$lib/quiz_types';
import Spinner from '$lib/Spinner.svelte';
@@ -20,31 +17,18 @@ SPDX-License-Identifier: MPL-2.0
const { t } = getLocalization();
const default_colors = ['#D6EDC9', '#B07156', '#7F7057', '#4E6E58'];
let question_results = $state(null);
let selected_question = $state(-1);
let timer_res: string = $state();
let shown_question_now: number = $state();
let final_results_clicked = $state(false);
let timer_interval: NodeJS.Timeout;
let answer_count = $state(0);
interface Props {
game_token: string;
quiz_data: QuizData;
bg_color: string;
final_results?: Array<null> | Array<Array<PlayerAnswer>>;
control_visible: boolean;
player_scores: any;
game_state: GameState;
}
let {
game_token,
quiz_data = $bindable(),
bg_color,
final_results = $bindable([null]),
control_visible,
player_scores = $bindable(),
game_state = $bindable()
}: Props = $props();
@@ -52,70 +36,51 @@ SPDX-License-Identifier: MPL-2.0
console.log('get_question_results');
});
socket.on('set_question_number', (data) => {
timer_res = '0';
clearInterval(timer_interval);
question_results = null;
shown_question_now = data.question_index;
timer_res = quiz_data.questions[data.question_index].time;
selected_question = selected_question + 1;
answer_count = 0;
timer(timer_res);
game_state.timer_res = '0';
game_state.question_results = null;
game_state.shown_question_now = data.question_index;
game_state.timer_res = quiz_data.questions[data.question_index].time;
game_state.timer_res = game_state.quiz_data.questions[data.question_index].time;
game_state.selected_question = game_state.selected_question + 1;
game_state.answer_count = 0;
clearInterval(timer_interval);
timer(game_state.timer_res);
});
socket.on('solutions', (_) => {
timer_res = '0';
clearInterval(timer_interval);
game_state.timer_res = '0';
clearInterval(timer_interval);
});
socket.on('final_results', (data) => {
// data = JSON.parse(data);
final_results_clicked = true;
timer_res = '0';
final_results = data;
game_state.final_results = data;
game_state.timer_res = '0';
game_state.final_results = data;
});
socket.on('everyone_answered', (_) => {
timer_res = '0';
game_state.timer_res = '0';
});
socket.on('question_results', (data) => {
console.log('question_results:', data);
question_results = data;
timer_res = '0';
game_state.question_results = data;
game_state.timer_res = '0';
});
socket.on('player_answer', (_) => {
answer_count += 1;
game_state.answer_count += 1;
});
const timer = (time: string) => {
let seconds = Number(time);
timer_interval = setInterval(() => {
if (timer_res === '0') {
if (game_state.timer_res === '0') {
clearInterval(timer_interval);
return;
} else {
seconds--;
}
timer_res = seconds.toString();
game_state.timer_res = seconds.toString();
}, 1000);
};
@@ -123,57 +88,51 @@ SPDX-License-Identifier: MPL-2.0
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
</script>
{#if control_visible}
{#if game_state.control_visible}
<Controls
{bg_color}
{selected_question}
{quiz_data}
bind:timer_res
{final_results}
{socket_game_controls}
{question_results}
{game_token}
{shown_question_now}
bind:game_state
/>
{/if}
{#if timer_res !== '0' && selected_question >= 0}
{#if game_state.timer_res !== '0' && game_state.selected_question >= 0}
<span
class="fixed top-0 bg-red-500 h-8 transition-all"
class:mt-10={control_visible}
style="width: {(100 / parseInt(quiz_data.questions[selected_question].time)) *
parseInt(timer_res)}vw"
class:mt-10={game_state.control_visible}
style="width: {(100 / parseInt(game_state.quiz_data.questions[game_state.selected_question].time)) *
parseInt(game_state.timer_res)}vw"
></span>
{/if}
<div class="w-full h-full" class:pt-28={control_visible} class:pt-12={!control_visible}>
{#if timer_res !== undefined && !final_results_clicked && !question_results}
<div class="w-full h-full" class:pt-28={game_state.control_visible} class:pt-12={!game_state.control_visible}>
{#if game_state.timer_res !== undefined && !final_results_clicked && !game_state.question_results}
<!-- Question is shown -->
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
{#await import('$lib/play/admin/slide.svelte')}
<Spinner my_20={false} />
{:then c}
<c.default question={quiz_data.questions[selected_question]} />
<c.default question={game_state.quiz_data.questions[game_state.selected_question]} />
{/await}
{:else}
<Question {quiz_data} {selected_question} {timer_res} {answer_count} {default_colors} />
<Question quiz_data={game_state.quiz_data} selected_question={game_state.selected_question} timer_res={game_state.timer_res} answer_count={game_state.answer_count} default_colors={default_colors} />
{/if}
{/if}
<br />
{#if timer_res === '0' && JSON.stringify(final_results) === JSON.stringify( [null] ) && quiz_data.questions[selected_question].type !== QuizQuestionType.SLIDE && question_results !== null && quiz_data.questions[selected_question]?.hide_results !== true}
{#if question_results === undefined}
{#if game_state.timer_res === '0' && JSON.stringify(game_state.final_results) === JSON.stringify( [null] ) && game_state.quiz_data.questions[game_state.selected_question].type !== QuizQuestionType.SLIDE && game_state.question_results !== null && game_state.quiz_data.questions[game_state.selected_question]?.hide_results !== true}
{#if game_state.question_results === undefined}
{#if !final_results_clicked}
<div class="w-full flex justify-center">
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
</div>
{/if}
{:else if quiz_data.questions[selected_question].type === QuizQuestionType.VOTING}
{:else if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.VOTING}
{#await import('$lib/play/admin/voting_results.svelte')}
<Spinner />
{:then c}
<c.default
data={question_results}
question={quiz_data.questions[selected_question]}
data={game_state.question_results}
question={game_state.quiz_data.questions[game_state.selected_question]}
/>
{/await}
{:else}
@@ -181,24 +140,24 @@ SPDX-License-Identifier: MPL-2.0
<Spinner />
{:then c}
<c.default
bind:data={player_scores}
question={quiz_data.questions[selected_question]}
new_data={question_results}
bind:data={game_state.player_scores}
question={game_state.quiz_data.questions[game_state.selected_question]}
new_data={game_state.question_results}
/>
{/await}
{/if}
{/if}
<br />
{#if selected_question === -1}
{#if game_state.selected_question === -1}
<div class="flex flex-col justify-center w-screen h-full">
<h1 class="text-7xl text-center">{@html quiz_data.title}</h1>
<p class="text-3xl pt-8 text-center">{@html quiz_data.description}</p>
{#if quiz_data.cover_image}
<h1 class="text-7xl text-center">{@html game_state.quiz_data.title}</h1>
<p class="text-3xl pt-8 text-center">{@html game_state.quiz_data.description}</p>
{#if game_state.quiz_data.cover_image}
<div class="flex justify-center align-middle items-center">
<div class="h-[30vh] m-auto w-auto mt-12">
<img
class="max-h-full max-w-full block"
src="/api/v1/storage/download/{quiz_data.cover_image}"
src="/api/v1/storage/download/{game_state.quiz_data.cover_image}"
alt="Not provided"
/>
</div>
+21 -36
View File
@@ -6,35 +6,21 @@ SPDX-License-Identifier: MPL-2.0
<script lang="ts">
import { QuizQuestionType } from '$lib/quiz_types';
import type { QuizData } from '$lib/quiz_types';
import type { Socket } from 'socket.io-client';
import { getLocalization } from '$lib/i18n';
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
import { GameState } from '$lib/play/admin/game_state.ts';
interface Props {
bg_color: string;
selected_question: number;
quiz_data: QuizData;
timer_res: string;
final_results: any;
socket_game_controls: SocketGameControls;
game_token: string;
question_results: any;
shown_question_now: number;
game_state: GameState;
}
let {
bg_color,
selected_question,
quiz_data,
timer_res = $bindable(),
final_results,
socket_game_controls,
game_token,
question_results,
shown_question_now,
game_state = $bindable()
}: Props = $props();
@@ -42,7 +28,6 @@ SPDX-License-Identifier: MPL-2.0
const show_solutions = () => {
socket_game_controls.show_solutions();
timer_res = '0';
game_state.timer_res = '0';
};
</script>
@@ -53,60 +38,60 @@ SPDX-License-Identifier: MPL-2.0
class:text-black={bg_color}
>
<p class="mr-auto ml-0 col-start-1 col-end-1">
{selected_question === -1 ? '0' : selected_question + 1}
/{quiz_data.questions.length}
{game_state.selected_question === -1 ? '0' : game_state.selected_question + 1}
/{game_state.quiz_data.questions.length}
</p>
<div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3">
{#if selected_question + 1 === quiz_data.questions.length && ((timer_res === '0' && question_results !== null) || quiz_data?.questions?.[selected_question]?.type === QuizQuestionType.SLIDE)}
{#if JSON.stringify(final_results) === JSON.stringify([null])}
{#if game_state.selected_question + 1 === game_state.quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || game_state.quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)}
{#if JSON.stringify(game_state.final_results) === JSON.stringify([null])}
<button onclick={() => socket_game_controls.get_final_results()} class="admin-button"
>{$t('admin_page.get_final_results')}
</button>
{/if}
{:else if timer_res === '0' || selected_question === -1}
{#if (selected_question + 1 !== quiz_data.questions.length && question_results !== null) || selected_question === -1}
{:else if game_state.timer_res === '0' || game_state.selected_question === -1}
{#if (game_state.selected_question + 1 !== game_state.quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1}
<button
onclick={() => {
socket_game_controls.set_question_number(selected_question + 1);
socket_game_controls.set_question_number(game_state.selected_question + 1);
}}
class="admin-button"
>{$t('admin_page.next_question', { question: selected_question + 2 })}
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
</button>
{/if}
{#if question_results === null && selected_question !== -1}
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
{#if game_state.question_results === null && game_state.selected_question !== -1}
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
<button
onclick={() => {
socket_game_controls.set_question_number(selected_question + 1);
socket_game_controls.set_question_number(game_state.selected_question + 1);
}}
class="admin-button"
>{$t('admin_page.next_question', { question: selected_question + 2 })}
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
</button>
{:else if quiz_data.questions[selected_question]?.hide_results === true}
{:else if game_state.quiz_data.questions[game_state.selected_question]?.hide_results === true}
<button
onclick={() => {
socket_game_controls.get_question_results(game_token, shown_question_now);
socket_game_controls.get_question_results(game_token, game_state.shown_question_now);
setTimeout(() => {
socket_game_controls.set_question_number(selected_question + 1);
socket_game_controls.set_question_number(game_state.selected_question + 1);
}, 200);
}}
class="admin-button"
>{$t('admin_page.next_question', { question: selected_question + 2 })}
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
</button>
{:else}
<button onclick={() => socket_game_controls.get_question_results(game_token, shown_question_now)} class="admin-button"
<button onclick={() => socket_game_controls.get_question_results(game_token, game_state.shown_question_now)} class="admin-button"
>{$t('admin_page.show_results')}
</button>
{/if}
{/if}
{:else if selected_question !== -1}
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
{:else if game_state.selected_question !== -1}
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
<button
onclick={() => {
socket_game_controls.set_question_number(selected_question + 1);
socket_game_controls.set_question_number(game_state.selected_question + 1);
}}
class="admin-button"
>{$t('admin_page.next_question', { question: selected_question + 2 })}
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
</button>
{:else}
<button onclick={show_solutions} class="admin-button"
@@ -11,15 +11,16 @@ SPDX-License-Identifier: MPL-2.0
import GrayButton from '$lib/components/buttons/gray.svelte';
import { fade } from 'svelte/transition';
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
import { GameState } from '$lib/play/admin/game_state';
interface Props {
game_pin: string;
players: any;
game_state: GameState;
socket_game_controls: SocketGameControls;
cqc_code: string;
}
let { game_pin, players = $bindable(), socket_game_controls, cqc_code = $bindable() }: Props = $props();
let { game_pin, game_state = $bindable(), socket_game_controls, cqc_code = $bindable() }: Props = $props();
let fullscreen_open = $state(false);
const { t } = getLocalization();
@@ -55,13 +56,13 @@ SPDX-License-Identifier: MPL-2.0
<div class="m-auto">
<div class="flex justify-center my-4">
<p class="m-auto text-2xl">
{#if players.length <= 1}
{#if game_state.players.length <= 1}
{$t('play_page.players_waiting', {
count: players.length ?? 0
count: game_state.players.length ?? 0
})}
{:else}
{$t('play_page.players_waiting_plural', {
count: players.length ?? 0
count: game_state.players.length ?? 0
})}
{/if}
</p>
@@ -74,13 +75,13 @@ SPDX-License-Identifier: MPL-2.0
{:else}
<div class="flex justify-center">
<p class="m-auto text-2xl">
{#if players.length <= 1}
{#if game_state.players.length <= 1}
{$t('play_page.players_waiting', {
count: players.length ?? 0
count: game_state.players.length ?? 0
})}
{:else}
{$t('play_page.players_waiting_plural', {
count: players.length ?? 0
count: game_state.players.length ?? 0
})}
{/if}
</p>
@@ -93,7 +94,7 @@ SPDX-License-Identifier: MPL-2.0
<div class="flex justify-center w-full mt-4">
<div>
<GrayButton
disabled={players.length < 1}
disabled={game_state.players.length < 1}
onclick={() => {
socket_game_controls.start_game()
}}
@@ -102,13 +103,13 @@ SPDX-License-Identifier: MPL-2.0
</div>
</div>
<div class="flex flex-row w-full mt-4 px-10 flex-wrap">
{#if players.length > 0}
{#each players as player}
{#if game_state.players.length > 0}
{#each game_state.players as player}
<div class="p-2 m-2 border-2 border-[#B07156] rounded-sm hover:cursor-pointer">
<span
class="hover:line-through text-lg"
onclick={() => {
socket_game_controls.kick_player(player.username, players);
socket_game_controls.kick_player(player.username, game_state.players);
}}>{player.username}</span
>
<!-- <button>{$t('words.kick')}</button>-->
+17 -21
View File
@@ -1,23 +1,19 @@
export class GameState {
public game_id: string;
public players: any[];
public selected_question: number;
public timer_res: string;
public question_results: any;
public answer_count: number;
public shown_question_now: number;
public final_results: any;
public game_started: boolean;
import type { Player, PlayerAnswer } from '$lib/admin.ts';
import { QuizData } from '$lib/quiz_types';
constructor(game_id: string, players: any[]) {
this.game_id = game_id;
this.players = players;
this.selected_question = -1;
this.timer_res = '0';
this.question_results = null;
this.answer_count = 0;
this.shown_question_now = -1;
this.final_results = null;
this.game_started = false;
}
export interface IGameState {
game_id: string;
players: Player[];
player_scores: Record<string, number>;
selected_question: number;
timer_res: string;
question_results: any;
answer_count: number;
shown_question_now: number;
final_results: Array<null> | Array<Array<PlayerAnswer>>;
game_started: boolean;
quiz_data: QuizData;
control_visible: boolean;
constructor(game_id: string);
}
+58 -41
View File
@@ -5,12 +5,9 @@ SPDX-License-Identifier: MPL-2.0
-->
<script lang="ts">
import type { QuizData } from '$lib/quiz_types';
import { socket } from '$lib/socket';
import { getLocalization } from '$lib/i18n';
import { navbarVisible } from '$lib/stores.svelte.ts';
import type { PlayerAnswer, Player } from '$lib/admin.ts';
import SomeAdminScreen from '$lib/admin.svelte';
import GameNotStarted from '$lib/play/admin/game_not_started.svelte';
import { browser } from '$app/environment';
@@ -19,7 +16,7 @@ SPDX-License-Identifier: MPL-2.0
import GrayButton from '$lib/components/buttons/gray.svelte';
import { page } from '$app/state';
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
import { GameState } from '$lib/play/admin/game_state.ts';
import { IGameState } from '$lib/play/admin/game_state.ts';
import { QuizQuestionType } from '$lib/quiz_types';
navbarVisible.visible = false;
@@ -35,25 +32,50 @@ SPDX-License-Identifier: MPL-2.0
data: any;
}
class GameState implements IGameState {
public game_id: string;
public players: Player[];
public player_scores: Record<string, number>;
public selected_question: number;
public timer_res: string;
public question_results: any;
public answer_count: number;
public shown_question_now: number;
public final_results: Array<null> | Array<Array<PlayerAnswer>>;
public game_started: boolean;
public quiz_data: QuizData;
public control_visible: boolean;
constructor(game_id: string) {
this.game_id = game_id;
this.players = $state([]);
this.player_scores = $state({});
this.selected_question = $state(-1);
this.timer_res = $state(undefined);
this.quiz_data = $state(null);
this.control_visible = $state(true);
this.shown_question_now = $state(-1);
this.final_results = $state([null]);
this.game_started = $state(false);
this.question_results = $state(null);
this.answer_count = $state(0);
}
}
let { data }: Props = $props();
let game_mode = $state();
let { auto_connect, game_token } = $state(data);
const game_pin = data.game_pin;
let players: Array<Player> = $state([]);
let player_scores = $state({});
let errorMessage = $state('');
let game_started = $state(false);
let quiz_data: QuizData = $state();
let control_visible = $state(true);
//let question_number = '0';
// let question_results = null;
let final_results: Array<null> | Array<Array<PlayerAnswer>> = $state([null]);
let success = $state(false);
let dataexport_download_a = $state();
let warnToLeave = true;
let export_token = $state(undefined);
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
let game_state: GameState = $state(new GameState(game_token));
const connect = async () => {
socket.emit('register_as_admin', {
game_pin: game_pin,
@@ -73,12 +95,12 @@ SPDX-License-Identifier: MPL-2.0
});
socket.on('registered_as_admin', (data) => {
quiz_data = JSON.parse(data['game']);
console.log(quiz_data);
game_state.quiz_data = JSON.parse(data['game']);
console.log(game_state.quiz_data);
success = true;
});
socket.on('player_joined', (int_data) => {
players = [...players, int_data];
game_state.players = [...game_state.players, int_data];
});
socket.on('already_registered_as_admin', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -87,11 +109,11 @@ SPDX-License-Identifier: MPL-2.0
});
socket.on('start_game', (_) => {
game_started = true;
game_state.game_started = true;
});
socket.on('control_visibility', (data) => {
control_visible = data.visible;
game_state.control_visible = data.visible;
});
/* socket.on('question_results', (int_data) => {
@@ -141,40 +163,39 @@ SPDX-License-Identifier: MPL-2.0
window.matchMedia('(prefers-color-scheme: dark)').matches);
}
let bg_color = $derived(quiz_data ? quiz_data.background_color : undefined);
let bg_image = $derived(quiz_data ? quiz_data.background_image : undefined);
let bg_color = $derived(game_state.quiz_data ? game_state.quiz_data.background_color : undefined);
let bg_image = $derived(game_state.quiz_data ? game_state.quiz_data.background_image : undefined);
let results_saved = $state(false);
let show_final_results = $derived(JSON.stringify(final_results) !== JSON.stringify([null]));
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
let game_state: GameState = $state(new GameState(game_token));
let show_final_results = $derived(JSON.stringify(game_state.final_results) !== JSON.stringify([null]));
// This function in called in every keyboard event in this page
const next_action = (e: KeyboardEvent) => {
if((e.key in ["Enter", " "])) return; // Don't catch events other than enter or spacebar
if(!game_started && players.length > 0) { // Game not started with conditions matched
console.log(game_state);
if(!game_state.game_started && game_state.players.length > 0) { // Game not started with conditions matched
socket_game_controls.start_game()
}
// This is a messy condition to check if we can show final results
else if(game_state.selected_question + 1 === quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)){
else if(game_state.selected_question + 1 === game_state.quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || game_state.quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)){
socket_game_controls.get_final_results();
}
// Game is ongoing and the current question is eather finished or quiz just started
// Game is ongoing and the current question is either finished or quiz just started
else if(game_state.timer_res === '0' || game_state.selected_question === -1) {
// We are eather in the beginning of the quiz or in the end of it
if((game_state.selected_question + 1 !== quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1) {
// We are either in the beginning of the quiz or in the end of it
if((game_state.selected_question + 1 !== game_state.quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1) {
socket_game_controls.set_question_number(game_state.selected_question + 1);
}
// Else we are in the middle of a question
else if(game_state.question_results === null && game_state.selected_question !== -1) {
if(quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE) {
if(game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE) {
socket_game_controls.set_question_number(game_state.selected_question + 1);
}
else if(quiz_data.questions[game_state.selected_question]?.hide_results === true) {
else if(game_state.quiz_data.questions[game_state.selected_question]?.hide_results === true) {
socket_game_controls.get_question_results(game_token, game_state.shown_question_now);
setTimeout(() => {
socket_game_controls.set_question_number(game_state.selected_question + 1);
@@ -188,7 +209,7 @@ SPDX-License-Identifier: MPL-2.0
// Game started, question not finished we check if the question is slide or not
else if(game_state.selected_question !== -1) {
switch(quiz_data.questions[game_state.selected_question].type) {
switch(game_state.quiz_data.questions[game_state.selected_question].type) {
case QuizQuestionType.SLIDE:
socket_game_controls.set_question_number(game_state.selected_question + 1);
break;
@@ -213,8 +234,8 @@ SPDX-License-Identifier: MPL-2.0
: `unset`}; background-color: {bg_color ? bg_color : 'transparent'}"
class:text-black={bg_color}
>
{#if JSON.stringify(final_results) !== JSON.stringify([null])}
{#if control_visible}
{#if JSON.stringify(game_state.final_results) !== JSON.stringify([null])}
{#if game_state.control_visible}
<div class="w-screen flex justify-center mt-16">
<div class="w-fit">
{#if export_token === undefined}
@@ -254,27 +275,23 @@ SPDX-License-Identifier: MPL-2.0
</div>
</div>
{/if}
<FinalResults bind:data={player_scores} {show_final_results} />
<FinalResults bind:data={game_state.player_scores} {show_final_results} />
{/if}
{#if !success}
{#if errorMessage !== ''}
<p class="text-red-700">{errorMessage}</p>
{/if}
{:else if !game_started}
{:else if !game_state.game_started}
<GameNotStarted
{game_pin}
bind:players
bind:game_state
{socket_game_controls}
cqc_code={page.url.searchParams.get('cqc_code')}
/>
{:else}
<SomeAdminScreen
bind:final_results
{game_token}
bind:quiz_data
{bg_color}
bind:player_scores
{control_visible}
bind:game_state
/>
{/if}