Files
4Gewinnt/GUI/src/routes/OnlineMode.vue
T
Jonas 7a073b6fff Handle disconnects, add restart flow and UI fixes
Server: Make DisconnectedPlayer async, await hub notifications, add logging, and delay scheduled deletion (2s). Harden ScheduleGameDeletion with try/catch and only destroy/send GameDestroyed when game is not running or has no players.

Client: Add restart-game flow — expose restartGame on OnlineGame, propagate event from GameEndedMenu (adds local restarted state and disables button), and hook restart handling + player disconnect on unmount in OnlineMode. Also conditionally show bot switch in GameCreationMenu and include replayGameCode in GameEnded interface. Remove automatic reconnect on SignalR connection.

Overall: Improves robustness around player disconnects and adds a UI/logic path for restarting games.
2026-03-12 23:20:05 +01:00

118 lines
3.2 KiB
Vue

<script setup lang="ts">
import CreateOrJoinMenu from '@/components/CreateOrJoinMenu.vue';
import Field from '@/components/game/Field.vue';
import InfoField from '@/components/game/InfoField.vue';
import GameCreationMenu from '@/components/GameCreationMenu.vue';
import GameEndedMenu from '@/components/GameEndedMenu.vue';
import GameJoinMenu from '@/components/GameJoinMenu.vue';
import type { GameSettings } from '@/scripts/interfaces/GameSettings';
import type JoinGameObject from '@/scripts/interfaces/JoinGameObject';
import OnlineGame from '@/scripts/logic/onlineMode/OnlineGame';
import type { GameEnded } from '@/scripts/logic/signalR/GameConnection';
import { onUnmounted, ref } from 'vue';
enum CurrentState {
CreateOrJoinSelection,
CreatingGame,
JoiningGame,
Game,
EndScreen,
}
let settings = ref<GameSettings>({
playerName1: 'Spieler 1',
fieldSize: { x: 7, y: 6 },
});
let joiningModel = ref<JoinGameObject>({
playerName: 'Spieler 2',
failed: false,
});
var currentState = ref<CurrentState>(CurrentState.CreateOrJoinSelection);
const game = ref<OnlineGame>(new OnlineGame());
const gameField = ref<number[][]>([]);
const currentSelectionIndex = ref<number | null>(null);
const gameEndedInformation = ref<GameEnded | null>(null);
game.value.onGameStateChanged = (gameState) => {
gameField.value = gameState?.currentField ?? [];
};
game.value.onGameEnded = (gameEndedInfo) => {
gameEndedInformation.value = gameEndedInfo;
currentState.value = CurrentState.EndScreen;
};
game.value.onGameCreated = (gameCode: string) => {
settings.value.message = `Das Spiel ist erstellt, deine Freund kann über folgendem Code Beitreten: ${gameCode}`;
};
game.value.onGameStarted = () => {
currentState.value = CurrentState.Game;
};
game.value.onGameJoinedFailed = () => {
joiningModel.value.failed = true;
};
async function createGame() {
await game.value.createGame(settings.value);
}
async function tryToJoin() {
await game.value.joinGame(joiningModel.value);
}
async function restartGame() {
if(!gameEndedInformation.value?.replayGameCode) return;
await game.value.restartGame(gameEndedInformation.value.replayGameCode);
}
onUnmounted(async () => {
await game.value.player.disconnect();
});
</script>
<template>
<CreateOrJoinMenu
v-if="currentState === CurrentState.CreateOrJoinSelection"
@createGame="currentState = CurrentState.CreatingGame"
@joinGame="currentState = CurrentState.JoiningGame"
/>
<GameCreationMenu
v-model:settings="settings"
@create-game="createGame"
v-else-if="
currentState === CurrentState.CreatingGame
"
/>
<GameJoinMenu
:joinGameObject="joiningModel"
v-else-if="currentState === CurrentState.JoiningGame"
@join="tryToJoin"
/>
<GameEndedMenu
:game-ended-information="gameEndedInformation"
v-else-if="currentState === CurrentState.EndScreen"
@restart-game="restartGame()"
></GameEndedMenu>
<div id="game" v-else>
<div class="game-content">
<Field
:game-state="gameField"
v-model:current-selection-index="currentSelectionIndex"
@click-on-game-field="game.drop(currentSelectionIndex ?? 1)"
/>
<InfoField :msg="game.currentDescription"></InfoField>
</div>
</div>
</template>
<style scoped></style>