7a073b6fff
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.
118 lines
3.2 KiB
Vue
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>
|