mirror of
https://github.com/Vale54321/schafkopf-bot.git
synced 2025-12-16 11:49:33 +01:00
Message types and server (#44)
* message type and handling * deleted web-content and fixed bug * edited main page
This commit is contained in:
committed by
GitHub
parent
cab2d36f48
commit
a0a1cfaa4a
@@ -1,235 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import {onMounted, ref} from 'vue';
|
||||
import CardComp from './components/CardComponent.vue';
|
||||
import {BackendMessage, Card, GamePhase, GameState} from "./BackendMessage";
|
||||
|
||||
const messageFromServer = ref<string[]>([]);
|
||||
|
||||
const gameStateText = ref<string>("Schafkopf");
|
||||
const gameInfoText = ref<string>("");
|
||||
|
||||
const socket = ref<WebSocket | null>();
|
||||
const tableCards = ref<Card[]>([]);
|
||||
const botCards = ref(0);
|
||||
const trickCard = ref<Card>();
|
||||
|
||||
const showGameSelect = ref(true);
|
||||
|
||||
|
||||
function startSimulation(): void {
|
||||
sendMessageToServer("startsimulation");
|
||||
|
||||
}
|
||||
|
||||
function startDedicated(): void {
|
||||
sendMessageToServer("startdedicated");
|
||||
|
||||
}
|
||||
|
||||
|
||||
function stopSimulation(): void {
|
||||
sendMessageToServer("stopsimulation");
|
||||
}
|
||||
|
||||
function showTrumpf(): void {
|
||||
tableCards.value = [];
|
||||
sendMessageToServer("showtrumpf");
|
||||
}
|
||||
|
||||
function showFarben(): void {
|
||||
tableCards.value = [];
|
||||
sendMessageToServer("showfarben");
|
||||
}
|
||||
|
||||
function setGame(game: string): void {
|
||||
sendMessageToServer(game);
|
||||
}
|
||||
|
||||
|
||||
function sendMessageToServer(message: string) {
|
||||
if (socket.value) {
|
||||
socket.value.send(message);
|
||||
console.log("Sent message to server:", message);
|
||||
}
|
||||
}
|
||||
|
||||
function showGameState(gamestate: GameState) {
|
||||
|
||||
|
||||
switch (gamestate.gamePhase) {
|
||||
case GamePhase.GAME_START:
|
||||
gameStateText.value = "Spiel startet";
|
||||
showGameSelect.value = false;
|
||||
botCards.value = 8;
|
||||
break;
|
||||
case GamePhase.TRICK_START:
|
||||
gameStateText.value = "Runde startet";
|
||||
tableCards.value = [];
|
||||
trickCard.value = undefined
|
||||
gameInfoText.value = "";
|
||||
break;
|
||||
case GamePhase.WAIT_FOR_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " muss eine Karte legen.";
|
||||
break;
|
||||
case GamePhase.PLAYER_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " hat eine Karte gespielt.";
|
||||
if (gamestate.currentPlayer === 0) {
|
||||
botCards.value--
|
||||
}
|
||||
if (gamestate.trumpf) {
|
||||
gameInfoText.value = "TRUMPF";
|
||||
} else {
|
||||
gameInfoText.value = gamestate.color?.toString() ?? "ERROR";
|
||||
}
|
||||
tableCards.value.push(gamestate.card!);
|
||||
|
||||
|
||||
break;
|
||||
case GamePhase.PLAYER_TRICK:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " sticht.";
|
||||
trickCard.value = gamestate.card
|
||||
break;
|
||||
case GamePhase.GAME_STOP:
|
||||
showGameSelect.value = true;
|
||||
break;
|
||||
default:
|
||||
gameStateText.value = "Fehler";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const websocketIp = import.meta.env.VITE_APP_WEBSOCKET_IP;
|
||||
// Open a WebSocket connection when the component is mounted
|
||||
socket.value = new WebSocket("ws://" + websocketIp + ":8080/schafkopf-events/");
|
||||
|
||||
// Handle connection opened
|
||||
socket.value.addEventListener("open", (event) => {
|
||||
console.log("WebSocket connection opened:", event);
|
||||
});
|
||||
|
||||
// Handle messages received from the server
|
||||
socket.value.addEventListener("message", (event) => {
|
||||
const message: BackendMessage = JSON.parse(event.data);
|
||||
console.log(message)
|
||||
if ('gamestate' in message) {
|
||||
console.log(message.gamestate)
|
||||
showGameState(message.gamestate)
|
||||
} else {
|
||||
console.log("Invalid BackendMessage format: ", event);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle connection closed
|
||||
socket.value.addEventListener("close", (event) => {
|
||||
console.log("WebSocket connection closed:", event);
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
socket.value.addEventListener("error", (event) => {
|
||||
console.error("WebSocket error:", event);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="message in messageFromServer" :key="message">{{ message }}</div>
|
||||
|
||||
<div v-if="showGameSelect">
|
||||
<!-- <div class="flex gap-2 place-content-center">-->
|
||||
<!-- <button @click="setGame('setgame:sauspiel')">Sauspiel</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:herzsolo')">herzsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:eichelsolo')">eichelsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattsolo')">blattsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellsolo')">schellsolo</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:eichelwenz')">eichelwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattwenz')">blattwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:herzwenz')">herzwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellwenz')">schellwenz</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:eichelgeier')">eichelgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattgeier')">blattgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:herzgeier')">herzgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellgeier')">schellgeier</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:geier')">Geier</button>-->
|
||||
<!-- <button @click="setGame('setgame:wenz')">Wenz</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="flex gap-2 place-content-center">-->
|
||||
<!-- <button @click="showFarben">Zeige alle Farben</button>-->
|
||||
<!-- <button @click="showTrumpf">Zeige alle Trumpfkarten</button>-->
|
||||
<!-- </div>-->
|
||||
<div class="flex gap-2 place-content-center">
|
||||
<button class="v-button" @click="startDedicated">Dedicated Starten</button>
|
||||
<button class="v-button" @click="startSimulation">Starten</button>
|
||||
<button class="v-button" @click="stopSimulation">Stoppen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="flex gap-2 place-content-center">
|
||||
<button class="v-button" @click="stopSimulation">Stoppen</button>
|
||||
</div>
|
||||
<h1 class=" top-52 text-white font-bold text-6xl absolute text-center w-full">{{ gameInfoText }}</h1>
|
||||
<h1 class=" top-64 text-white font-bold text-6xl absolute text-center w-full">{{ gameStateText }}</h1>
|
||||
<div v-if="tableCards.length > 0">
|
||||
<!-- <div class="grid grid-cols-4 place-content-center">-->
|
||||
<!-- <CardComp v-for="card in tableCards" :card="card" class="md" />-->
|
||||
<!-- </div>-->
|
||||
<CardComp v-if="tableCards.length > 0" :card="tableCards[0]" class="absolute card1 md"/>
|
||||
<CardComp v-if="tableCards.length > 1" :card="tableCards[1]" class="absolute card2 md"/>
|
||||
<CardComp v-if="tableCards.length > 2" :card="tableCards[2]" class="absolute card3 md"/>
|
||||
<CardComp v-if="tableCards.length > 3" :card="tableCards[3]" class="absolute card4 md"/>
|
||||
</div>
|
||||
<div class="absolute left-0 top-1/2 transform -translate-y-1/2">
|
||||
<CardComp v-if="trickCard" :card="trickCard" class="xl"/>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 w-full">
|
||||
<div class="flex flex-row gap-3 w-fit mx-auto justify-center">
|
||||
<CardComp v-for="i in botCards" :key="i" :card="Card.BACK" class="sm"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-view/>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
$card-height: 24rem;
|
||||
|
||||
.card0 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card1 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card2 {
|
||||
z-index: 2;
|
||||
top: calc(50% + ($card-height * 0.10));
|
||||
left: calc(50% - ($card-height * 0.3));
|
||||
transform: rotate(50deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card3 {
|
||||
z-index: 3;
|
||||
top: calc(50% - ($card-height * 0.125));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-30deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card4 {
|
||||
z-index: 4;
|
||||
top: calc(50% - ($card-height * 0.4));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-60deg) translate(-50%, -50%);
|
||||
}
|
||||
@import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
</style>
|
||||
|
||||
@@ -71,6 +71,13 @@ export enum GamePhase {
|
||||
PLAYER_TRICK = "PLAYER_TRICK"
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
PLAYER_CARD = "PLAYER_CARD",
|
||||
START_DEDICATED_GAME = "START_DEDICATED_GAME",
|
||||
JOIN_GAME = "JOIN_GAME",
|
||||
REQUEST_SERVER_CONNECTION = "REQUEST_SERVER_CONNECTION",
|
||||
}
|
||||
|
||||
// Define the interface for an array of cards
|
||||
export interface CardArray {
|
||||
cards: Card[];
|
||||
@@ -92,8 +99,13 @@ export interface GameState {
|
||||
|
||||
|
||||
export interface GameStateJson {
|
||||
gamestate: GameState
|
||||
gamestate: GameState,
|
||||
}
|
||||
|
||||
export interface EmptyMessage {
|
||||
message_type: string;
|
||||
content: GameStateJson | CardArray | CardObject;
|
||||
}
|
||||
|
||||
// Define a union type for all possible message types
|
||||
export type BackendMessage = CardObject | CardArray | GameStateJson;
|
||||
export type BackendMessage = EmptyMessage
|
||||
@@ -21,7 +21,7 @@ watch(() => props.card, (newCard) => {
|
||||
<template>
|
||||
<div
|
||||
:class="{'!scale-105 !z-10 !top-1/2 !left-1/2' : focus}" class="card transition overflow-hidden"
|
||||
@click="focus=!focus">
|
||||
>
|
||||
<img class="h-full rounded-[1rem] mx-auto" :src="imgSrc" alt="card">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import { createApp } from 'vue'
|
||||
import {createApp} from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import {createRouter, createWebHistory} from "vue-router";
|
||||
import {setupService} from "./services/DependencyInjection.ts";
|
||||
|
||||
createApp(App).mount('#app')
|
||||
const routes = [
|
||||
{path: '/', component: () => import('./pages/MainMenu.vue'),},
|
||||
{path: '/online', component: () => import('./pages/OnlineGameList.vue'),},
|
||||
{path: '/localgame', component: () => import('./pages/LocalGame.vue'),},
|
||||
{path: '/dedicatedgame', component: () => import('./pages/DedicatedGame.vue'),},
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
})
|
||||
|
||||
const websocketIp = import.meta.env.VITE_APP_WEBSOCKET_IP;
|
||||
setupService("ws://" + websocketIp + ":8080/schafkopf-events/");
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
|
||||
181
Frontend/src/pages/DedicatedGame.vue
Normal file
181
Frontend/src/pages/DedicatedGame.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<script lang="ts" setup>
|
||||
import {onMounted, ref} from 'vue';
|
||||
|
||||
import {scg} from 'ioc-service-container';
|
||||
import CardComp from '../components/CardComponent.vue';
|
||||
import {BackendMessage, Card, GamePhase, GameState, MessageType} from "../BackendMessage";
|
||||
|
||||
const backendConnection = scg("BackendConnection");
|
||||
|
||||
|
||||
const messageFromServer = ref<string[]>([]);
|
||||
|
||||
const gameStateText = ref<string>("Schafkopf");
|
||||
const gameInfoText = ref<string>("");
|
||||
|
||||
const socket = ref<WebSocket | null>();
|
||||
const tableCards = ref<Card[]>([]);
|
||||
const botCards = ref<Card[]>();
|
||||
const trickCard = ref<Card>();
|
||||
|
||||
const showGameSelect = ref(true);
|
||||
|
||||
function startDedicated(): void {
|
||||
backendConnection.sendMessage(MessageType.START_DEDICATED_GAME,);
|
||||
}
|
||||
|
||||
function joinGame(): void {
|
||||
backendConnection.sendMessage(MessageType.JOIN_GAME,);
|
||||
}
|
||||
|
||||
function sendCard(cardInput: Card): void {
|
||||
const index = botCards.value.findIndex(card => card === cardInput);
|
||||
|
||||
// If card exists in the array, remove it
|
||||
if (index !== -1) {
|
||||
botCards.value.splice(index, 1);
|
||||
}
|
||||
backendConnection.sendMessage(MessageType.PLAYER_CARD, {card: cardInput});
|
||||
}
|
||||
|
||||
function showGameState(gamestate: GameState) {
|
||||
|
||||
|
||||
switch (gamestate.gamePhase) {
|
||||
case GamePhase.GAME_START:
|
||||
gameStateText.value = "Spiel startet";
|
||||
showGameSelect.value = false;
|
||||
// botCards.value = 0;
|
||||
break;
|
||||
case GamePhase.TRICK_START:
|
||||
gameStateText.value = "Runde startet";
|
||||
tableCards.value = [];
|
||||
trickCard.value = undefined
|
||||
gameInfoText.value = "";
|
||||
break;
|
||||
case GamePhase.WAIT_FOR_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " muss eine Karte legen.";
|
||||
break;
|
||||
case GamePhase.PLAYER_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " hat eine Karte gespielt.";
|
||||
if (gamestate.currentPlayer === 0) {
|
||||
// botCards.value.pop();
|
||||
}
|
||||
if (gamestate.trumpf) {
|
||||
gameInfoText.value = "TRUMPF";
|
||||
} else {
|
||||
gameInfoText.value = gamestate.color?.toString() ?? "ERROR";
|
||||
}
|
||||
tableCards.value.push(gamestate.card!);
|
||||
|
||||
|
||||
break;
|
||||
case GamePhase.PLAYER_TRICK:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " sticht.";
|
||||
trickCard.value = gamestate.card
|
||||
break;
|
||||
case GamePhase.GAME_STOP:
|
||||
showGameSelect.value = true;
|
||||
break;
|
||||
default:
|
||||
gameStateText.value = "Fehler";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
socket.value = backendConnection.getWebSocket();
|
||||
|
||||
const messageListener = (message: string) => {
|
||||
const message1: BackendMessage = JSON.parse(message);
|
||||
console.log(message1)
|
||||
if (message1.message_type === "GAME_STATE") {
|
||||
console.log(message1.content)
|
||||
showGameState(message1.content)
|
||||
}
|
||||
if (message1.message_type === "ONLINE_PLAYER_HAND") {
|
||||
botCards.value = message1.content.cards;
|
||||
console.log(message1.content.cards)
|
||||
}
|
||||
};
|
||||
|
||||
backendConnection.addMessageListener(messageListener);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="message in messageFromServer" :key="message">{{ message }}</div>
|
||||
|
||||
<div v-if="showGameSelect">
|
||||
<div class="flex gap-2 place-content-center">
|
||||
<button class="v-button" @click="startDedicated">Starten</button>
|
||||
<button class="v-button" @click="joinGame">Join</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="flex gap-2 place-content-center">
|
||||
</div>
|
||||
<h1 class=" top-52 text-white font-bold text-6xl absolute text-center w-full">{{ gameInfoText }}</h1>
|
||||
<h1 class=" top-64 text-white font-bold text-6xl absolute text-center w-full">{{ gameStateText }}</h1>
|
||||
<div v-if="tableCards.length > 0">
|
||||
<!-- <div class="grid grid-cols-4 place-content-center">-->
|
||||
<!-- <CardComp v-for="card in tableCards" :card="card" class="md" />-->
|
||||
<!-- </div>-->
|
||||
<CardComp v-if="tableCards.length > 0" :card="tableCards[0]" class="absolute card1 md"/>
|
||||
<CardComp v-if="tableCards.length > 1" :card="tableCards[1]" class="absolute card2 md"/>
|
||||
<CardComp v-if="tableCards.length > 2" :card="tableCards[2]" class="absolute card3 md"/>
|
||||
<CardComp v-if="tableCards.length > 3" :card="tableCards[3]" class="absolute card4 md"/>
|
||||
</div>
|
||||
<div class="absolute left-0 top-1/2 transform -translate-y-1/2">
|
||||
<CardComp v-if="trickCard" :card="trickCard" class="xl"/>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 w-full">
|
||||
<div class="flex flex-row gap-3 w-fit mx-auto justify-center">
|
||||
<CardComp v-for="card in botCards" :card="card" class="sm" @click="sendCard(card)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-view/>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
$card-height: 24rem;
|
||||
|
||||
.card0 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card1 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card2 {
|
||||
z-index: 2;
|
||||
top: calc(50% + ($card-height * 0.10));
|
||||
left: calc(50% - ($card-height * 0.3));
|
||||
transform: rotate(50deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card3 {
|
||||
z-index: 3;
|
||||
top: calc(50% - ($card-height * 0.125));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-30deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card4 {
|
||||
z-index: 4;
|
||||
top: calc(50% - ($card-height * 0.4));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-60deg) translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
222
Frontend/src/pages/LocalGame.vue
Normal file
222
Frontend/src/pages/LocalGame.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<script lang="ts" setup>
|
||||
import {onMounted, ref} from 'vue';
|
||||
|
||||
import {scg} from 'ioc-service-container';
|
||||
import CardComp from '../components/CardComponent.vue';
|
||||
import {BackendMessage, Card, GamePhase, GameState} from "../BackendMessage";
|
||||
|
||||
const backendConnection = scg("BackendConnection");
|
||||
|
||||
|
||||
const messageFromServer = ref<string[]>([]);
|
||||
|
||||
const gameStateText = ref<string>("Schafkopf");
|
||||
const gameInfoText = ref<string>("");
|
||||
|
||||
const socket = ref<WebSocket | null>();
|
||||
const tableCards = ref<Card[]>([]);
|
||||
const botCards = ref(0);
|
||||
const trickCard = ref<Card>();
|
||||
|
||||
const showGameSelect = ref(true);
|
||||
|
||||
|
||||
function startSimulation(): void {
|
||||
sendMessageToServer("startsimulation");
|
||||
|
||||
}
|
||||
|
||||
function startDedicated(): void {
|
||||
sendMessageToServer("startdedicated");
|
||||
|
||||
}
|
||||
|
||||
|
||||
function stopSimulation(): void {
|
||||
sendMessageToServer("stopsimulation");
|
||||
}
|
||||
|
||||
function showTrumpf(): void {
|
||||
tableCards.value = [];
|
||||
sendMessageToServer("showtrumpf");
|
||||
}
|
||||
|
||||
function showFarben(): void {
|
||||
tableCards.value = [];
|
||||
sendMessageToServer("showfarben");
|
||||
}
|
||||
|
||||
function setGame(game: string): void {
|
||||
sendMessageToServer(game);
|
||||
}
|
||||
|
||||
|
||||
function sendMessageToServer(message: string) {
|
||||
if (socket.value) {
|
||||
socket.value.send(message);
|
||||
console.log("Sent message to server:", message);
|
||||
}
|
||||
}
|
||||
|
||||
function showGameState(gamestate: GameState) {
|
||||
|
||||
|
||||
switch (gamestate.gamePhase) {
|
||||
case GamePhase.GAME_START:
|
||||
gameStateText.value = "Spiel startet";
|
||||
showGameSelect.value = false;
|
||||
botCards.value = 8;
|
||||
break;
|
||||
case GamePhase.TRICK_START:
|
||||
gameStateText.value = "Runde startet";
|
||||
tableCards.value = [];
|
||||
trickCard.value = undefined
|
||||
gameInfoText.value = "";
|
||||
break;
|
||||
case GamePhase.WAIT_FOR_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " muss eine Karte legen.";
|
||||
break;
|
||||
case GamePhase.PLAYER_CARD:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " hat eine Karte gespielt.";
|
||||
if (gamestate.currentPlayer === 0) {
|
||||
botCards.value--
|
||||
}
|
||||
if (gamestate.trumpf) {
|
||||
gameInfoText.value = "TRUMPF";
|
||||
} else {
|
||||
gameInfoText.value = gamestate.color?.toString() ?? "ERROR";
|
||||
}
|
||||
tableCards.value.push(gamestate.card!);
|
||||
|
||||
|
||||
break;
|
||||
case GamePhase.PLAYER_TRICK:
|
||||
gameStateText.value = "Spieler " + gamestate.currentPlayer + " sticht.";
|
||||
trickCard.value = gamestate.card
|
||||
break;
|
||||
case GamePhase.GAME_STOP:
|
||||
showGameSelect.value = true;
|
||||
break;
|
||||
default:
|
||||
gameStateText.value = "Fehler";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
socket.value = backendConnection.getWebSocket();
|
||||
|
||||
const messageListener = (message: string) => {
|
||||
const message1: BackendMessage = JSON.parse(message);
|
||||
console.log(message1)
|
||||
if ('gamestate' in message1) {
|
||||
console.log(message1.gamestate)
|
||||
showGameState(message1.gamestate)
|
||||
}
|
||||
};
|
||||
|
||||
backendConnection.addMessageListener(messageListener);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="message in messageFromServer" :key="message">{{ message }}</div>
|
||||
|
||||
<div v-if="showGameSelect">
|
||||
<!-- <div class="flex gap-2 place-content-center">-->
|
||||
<!-- <button @click="setGame('setgame:sauspiel')">Sauspiel</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:herzsolo')">herzsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:eichelsolo')">eichelsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattsolo')">blattsolo</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellsolo')">schellsolo</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:eichelwenz')">eichelwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattwenz')">blattwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:herzwenz')">herzwenz</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellwenz')">schellwenz</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:eichelgeier')">eichelgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:blattgeier')">blattgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:herzgeier')">herzgeier</button>-->
|
||||
<!-- <button @click="setGame('setgame:schellgeier')">schellgeier</button>-->
|
||||
|
||||
<!-- <button @click="setGame('setgame:geier')">Geier</button>-->
|
||||
<!-- <button @click="setGame('setgame:wenz')">Wenz</button>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="flex gap-2 place-content-center">-->
|
||||
<!-- <button @click="showFarben">Zeige alle Farben</button>-->
|
||||
<!-- <button @click="showTrumpf">Zeige alle Trumpfkarten</button>-->
|
||||
<!-- </div>-->
|
||||
<div class="flex gap-2 place-content-center">
|
||||
<button class="v-button" @click="startSimulation">Starten</button>
|
||||
<button class="v-button" @click="stopSimulation">Stoppen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="flex gap-2 place-content-center">
|
||||
<button class="v-button" @click="stopSimulation">Stoppen</button>
|
||||
</div>
|
||||
<h1 class=" top-52 text-white font-bold text-6xl absolute text-center w-full">{{ gameInfoText }}</h1>
|
||||
<h1 class=" top-64 text-white font-bold text-6xl absolute text-center w-full">{{ gameStateText }}</h1>
|
||||
<div v-if="tableCards.length > 0">
|
||||
<!-- <div class="grid grid-cols-4 place-content-center">-->
|
||||
<!-- <CardComp v-for="card in tableCards" :card="card" class="md" />-->
|
||||
<!-- </div>-->
|
||||
<CardComp v-if="tableCards.length > 0" :card="tableCards[0]" class="absolute card1 md"/>
|
||||
<CardComp v-if="tableCards.length > 1" :card="tableCards[1]" class="absolute card2 md"/>
|
||||
<CardComp v-if="tableCards.length > 2" :card="tableCards[2]" class="absolute card3 md"/>
|
||||
<CardComp v-if="tableCards.length > 3" :card="tableCards[3]" class="absolute card4 md"/>
|
||||
</div>
|
||||
<div class="absolute left-0 top-1/2 transform -translate-y-1/2">
|
||||
<CardComp v-if="trickCard" :card="trickCard" class="xl"/>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 w-full">
|
||||
<div class="flex flex-row gap-3 w-fit mx-auto justify-center">
|
||||
<CardComp v-for="i in botCards" :key="i" :card="Card.BACK" class="sm"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-view/>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
$card-height: 24rem;
|
||||
|
||||
.card0 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card1 {
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card2 {
|
||||
z-index: 2;
|
||||
top: calc(50% + ($card-height * 0.10));
|
||||
left: calc(50% - ($card-height * 0.3));
|
||||
transform: rotate(50deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card3 {
|
||||
z-index: 3;
|
||||
top: calc(50% - ($card-height * 0.125));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-30deg) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.card4 {
|
||||
z-index: 4;
|
||||
top: calc(50% - ($card-height * 0.4));
|
||||
left: calc(50% + ($card-height * 0.35));
|
||||
transform: rotate(-60deg) translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
187
Frontend/src/pages/MainMenu.vue
Normal file
187
Frontend/src/pages/MainMenu.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<script setup lang="ts">
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {scg} from "ioc-service-container";
|
||||
import {BackendMessage, MessageType} from "../BackendMessage.ts";
|
||||
|
||||
const backendConnection = scg("BackendConnection");
|
||||
|
||||
const serverAddress = ref("http://10.6.9.57:8085/")
|
||||
const isConnected = ref<boolean>(false);
|
||||
const isPingInProgress = ref<boolean>(false);
|
||||
const secondsRemaining = ref<number>(10);
|
||||
|
||||
const checkInterval: number = 10;
|
||||
const router = useRouter();
|
||||
|
||||
onBeforeMount(async () => {
|
||||
// Call checkConnection immediately and then every 10 seconds
|
||||
await checkConnectionWithCountdown();
|
||||
});
|
||||
|
||||
async function checkConnectionWithCountdown() {
|
||||
await checkConnection();
|
||||
setTimeout(checkConnectionWithCountdown, checkInterval * 1000);
|
||||
}
|
||||
|
||||
async function checkConnection(): Promise<void> {
|
||||
if (!isConnected.value) {
|
||||
isPingInProgress.value = true;
|
||||
}
|
||||
try {
|
||||
// Try to fetch a resource from the internet
|
||||
await fetch(serverAddress.value + "health", {mode: "no-cors"})
|
||||
// If successful, set isConnected to true
|
||||
isConnected.value = true;
|
||||
} catch (error) {
|
||||
// If an error occurs (e.g., network error), set isConnected to false
|
||||
isConnected.value = false;
|
||||
}
|
||||
isPingInProgress.value = false;
|
||||
|
||||
let countDown = checkInterval;
|
||||
secondsRemaining.value = countDown;
|
||||
|
||||
let countdownInterval = setTimeout(updateCountdown, 1000);
|
||||
|
||||
function updateCountdown() {
|
||||
secondsRemaining.value = --countDown;
|
||||
if (countDown <= 0) clearInterval(countdownInterval);
|
||||
else countdownInterval = setTimeout(updateCountdown, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
async function openOnlineGameList() {
|
||||
backendConnection.sendMessage(MessageType.REQUEST_SERVER_CONNECTION, {serverAddress: serverAddress.value});
|
||||
|
||||
// Create a Promise<void> that resolves when the success message is received
|
||||
const successMessageReceived = new Promise<void>((resolve) => {
|
||||
const messageListener = (message: string) => {
|
||||
const message1: BackendMessage = JSON.parse(message);
|
||||
console.log(message)
|
||||
if (message1.message_type === "SERVER_CONNECTION_SUCCESSFUL") {
|
||||
// Resolve the Promise when the success message is received
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
backendConnection.addMessageListener(messageListener);
|
||||
});
|
||||
|
||||
|
||||
// Wait for the success message to be received
|
||||
await successMessageReceived;
|
||||
|
||||
// Once the success message is received, route to '/online'
|
||||
await router.push("/online");
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col place-content-center h-screen gap-8 items-center">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 items-stretch px-2 gap-8">
|
||||
<div class="flex flex-col gap-6 bg-base-200 rounded-box p-8">
|
||||
<div class="flex flex-col gap-4 text-center">
|
||||
<h1 class="text-5xl font-bold">Lokales Spiel</h1>
|
||||
|
||||
<span class="text-sm">mit NFC Reader</span>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-person text-accent"></i>
|
||||
für 3-4 reale Spieler
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-robot text-accent"></i>
|
||||
ggf. 1 virtueller Mitspieler
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-6 items-center">
|
||||
<i class="bi bi-exclamation-circle text-accent"></i>
|
||||
NFC Reader erforderlich
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-primary">Spielen</a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6 bg-base-200 rounded-box p-8">
|
||||
<div class="flex flex-col gap-4 text-center">
|
||||
<h1 class="text-5xl font-bold">Online</h1>
|
||||
|
||||
<span class="text-sm">spiele gegen Spieler aus der ganzen Welt</span>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-person text-accent"></i>
|
||||
1 Spieler
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-pc-display text-accent"></i>
|
||||
2-3 online Mitspieler
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-robot text-accent"></i>
|
||||
ggf. 1-2 virtuelle Mitspieler
|
||||
</div>
|
||||
|
||||
<div v-if="isPingInProgress" class="flex gap-2 mt-6 items-center">
|
||||
<i class="bi bi bi-arrow-repeat text-info animate-spin"></i>
|
||||
Serververbindung wird hergestellt ...
|
||||
</div>
|
||||
|
||||
<div v-else-if="!isConnected" class="flex gap-2 mt-6 items-center">
|
||||
<i class="bi bi-x-lg text-error"></i>
|
||||
Serververbindung fehlgeschlagen ({{ secondsRemaining }} Sekunden)
|
||||
</div>
|
||||
|
||||
<div v-else class="flex gap-2 mt-6 items-center">
|
||||
<i class="bi bi-check2 text-success"></i>
|
||||
Verbindung zum Server hergestellt
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button :disabled="!isConnected" class="btn btn-primary" @click="openOnlineGameList()">Spielen</button>
|
||||
<div class="divider"></div>
|
||||
<input
|
||||
v-model="serverAddress"
|
||||
type="text" placeholder="Serveradresse" class="input input-bordered w-full max-w-xs"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6 bg-base-200 rounded-box p-8">
|
||||
<div class="flex flex-col gap-4 text-center">
|
||||
<h1 class="text-5xl font-bold">Übung</h1>
|
||||
|
||||
<span class="text-sm">spiele zum Üben gegen NPCs</span>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-person text-accent"></i>
|
||||
1 Spieler
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
<i class="bi bi-robot text-accent"></i>
|
||||
3 virtuelle Mitspieler
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-primary">Spielen</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
14
Frontend/src/pages/OnlineGameList.vue
Normal file
14
Frontend/src/pages/OnlineGameList.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<router-link to="/dedicatedgame">
|
||||
<button class="btn btn-primary">Zum Spiel</button>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
76
Frontend/src/services/BackendConnection.ts
Normal file
76
Frontend/src/services/BackendConnection.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {MessageType} from "../BackendMessage.ts";
|
||||
|
||||
interface JsonMessage {
|
||||
origin: string;
|
||||
message: any; // Adjust 'any' type as per your expected message structure
|
||||
}
|
||||
|
||||
export class BackendConnection {
|
||||
|
||||
|
||||
private readonly webSocket: WebSocket;
|
||||
private messageListeners: ((message: string) => void)[] = [];
|
||||
private backendUri: string;
|
||||
|
||||
|
||||
constructor(backendUri: string) {
|
||||
this.backendUri = backendUri;
|
||||
|
||||
this.webSocket = new WebSocket(backendUri);
|
||||
|
||||
// Registering event listener for message reception
|
||||
this.webSocket.addEventListener('message', this.handleMessage.bind(this));
|
||||
// Handle connection closed
|
||||
this.webSocket.addEventListener("close", (event) => {
|
||||
console.log("WebSocket connection closed:", event);
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
this.webSocket.addEventListener("error", (event) => {
|
||||
console.error("WebSocket error:", event);
|
||||
});
|
||||
}
|
||||
|
||||
public sendMessage(messageType: MessageType, message?: any): void {
|
||||
let jsonMessage;
|
||||
if (message === undefined) {
|
||||
jsonMessage = {
|
||||
origin: "FRONTEND",
|
||||
message: {
|
||||
message_type: messageType,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
jsonMessage = {
|
||||
origin: "FRONTEND",
|
||||
message: {
|
||||
message_type: messageType,
|
||||
content: message
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
console.log("Sending message:", jsonMessage);
|
||||
this.webSocket.send(JSON.stringify(jsonMessage));
|
||||
}
|
||||
|
||||
public getWebSocket(): WebSocket {
|
||||
return this.webSocket;
|
||||
}
|
||||
|
||||
public addMessageListener(listener: (message: string) => void): void {
|
||||
this.messageListeners.push(listener);
|
||||
}
|
||||
|
||||
public removeMessageListener(listener: (message: string) => void): void {
|
||||
this.messageListeners = this.messageListeners.filter(l => l !== listener);
|
||||
}
|
||||
|
||||
private handleMessage(event: MessageEvent): void {
|
||||
const message = event.data as string;
|
||||
// Notify all registered message listeners
|
||||
this.messageListeners.forEach(listener => {
|
||||
listener(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
15
Frontend/src/services/DependencyInjection.ts
Normal file
15
Frontend/src/services/DependencyInjection.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import {ServiceContainer} from "ioc-service-container";
|
||||
import {BackendConnection} from "./BackendConnection.ts";
|
||||
|
||||
type IoCTypes = {
|
||||
BackendConnection: BackendConnection,
|
||||
};
|
||||
|
||||
declare module 'ioc-service-container' {
|
||||
export function scg<T extends keyof IoCTypes, U extends IoCTypes[T]>(id: T): U;
|
||||
}
|
||||
|
||||
export function setupService(backendUri: string) {
|
||||
const backendConnection = new BackendConnection(backendUri);
|
||||
ServiceContainer.set('BackendConnection', () => backendConnection);
|
||||
}
|
||||
Reference in New Issue
Block a user