Support player names and online join flow
Move SignalR group join into GameManager and add support for player names and improved online join flow. - Moved Groups.AddToGroupAsync call out of GameHubSocket into GameManager (hubContext.Groups.AddToGroupAsync) so group membership is handled when adding players to a game. - GUI: added player name input to GameJoinMenu and updated JoinGameObject to include playerName and use string gameCode. - OnlineMode & LocalMode: constructors updated to instantiate games without passing settings; removed WaitingForOpponent state and cleaned up event bindings. - OnlineGame: rewrote to handle onGameCreated/onGameJoined/onGameStarted/onFieldUpdated/onGameEnded/onError, added joinGame validation (expects 6-digit code), join flow (connect + join), state updates, drop forwarding to player.drop(gameId, index) and place description updates. - LocalGame: constructor signature simplified to no-arg. These changes centralize group management, add player identification, validate join codes, and improve game state/event handling for online play.
This commit is contained in:
@@ -42,8 +42,6 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
return;
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, result);
|
||||
|
||||
await Clients.Caller.SendAsync("GameJoined", new
|
||||
{
|
||||
GameId = result,
|
||||
|
||||
@@ -3,6 +3,7 @@ using API.Models.DataClasses;
|
||||
using API.Models.Game;
|
||||
using API.Repository.GameRepo;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace API.Services.GameManager;
|
||||
|
||||
@@ -27,6 +28,8 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
return null;
|
||||
}
|
||||
|
||||
await hubContext.Groups.AddToGroupAsync(player.ConnectionId, game.Id);
|
||||
|
||||
if (game.Players.Count == 2)
|
||||
{
|
||||
game.StartGame();
|
||||
|
||||
@@ -17,6 +17,11 @@ let joiningModel = defineModel<JoinGameObject>('joinGameObject', {
|
||||
dein Freund gegeben hat.
|
||||
</h3>
|
||||
<v-divider class="mb-5 mt-5"></v-divider>
|
||||
<v-text-field
|
||||
v-model="joiningModel.playerName"
|
||||
label="Name vom Spieler 1"
|
||||
required
|
||||
></v-text-field>
|
||||
<v-otp-input
|
||||
length="6"
|
||||
v-model="joiningModel.gameCode"
|
||||
|
||||
@@ -21,7 +21,7 @@ let settings = ref<GameSettings>({
|
||||
});
|
||||
|
||||
var currentState = ref<CurrentState>(CurrentState.CreatingGame);
|
||||
const game = ref<LocalGame>(new LocalGame(settings.value));
|
||||
const game = ref<LocalGame>(new LocalGame());
|
||||
const gameField = ref<number[][]>([]);
|
||||
const currentSelectionIndex = ref<number | null>(null);
|
||||
const gameEndedInformation = ref<GameEnded | null>(null);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<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';
|
||||
@@ -13,7 +15,6 @@ enum CurrentState {
|
||||
CreateOrJoinSelection,
|
||||
CreatingGame,
|
||||
JoiningGame,
|
||||
WaitingForOpponent,
|
||||
Game,
|
||||
EndScreen,
|
||||
}
|
||||
@@ -24,11 +25,12 @@ let settings = ref<GameSettings>({
|
||||
});
|
||||
|
||||
let joiningModel = ref<JoinGameObject>({
|
||||
playerName: 'Spieler 2',
|
||||
failed: false,
|
||||
});
|
||||
|
||||
var currentState = ref<CurrentState>(CurrentState.CreateOrJoinSelection);
|
||||
const game = ref<OnlineGame>(new OnlineGame(settings.value));
|
||||
const game = ref<OnlineGame>(new OnlineGame());
|
||||
const gameField = ref<number[][]>([]);
|
||||
const currentSelectionIndex = ref<number | null>(null);
|
||||
const gameEndedInformation = ref<GameEnded | null>(null);
|
||||
@@ -46,11 +48,21 @@ 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() {}
|
||||
async function tryToJoin() {
|
||||
await game.value.joinGame(joiningModel.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -62,14 +74,16 @@ async function tryToJoin() {}
|
||||
|
||||
<GameCreationMenu
|
||||
v-model:settings="settings"
|
||||
@create-game="createGame()"
|
||||
v-else-if="currentState === CurrentState.CreatingGame || currentState === CurrentState.WaitingForOpponent"
|
||||
@create-game="createGame"
|
||||
v-else-if="
|
||||
currentState === CurrentState.CreatingGame
|
||||
"
|
||||
/>
|
||||
|
||||
<GameJoinMenu
|
||||
:joinGameObject="joiningModel"
|
||||
v-else-if="currentState === CurrentState.JoiningGame"
|
||||
@join="tryToJoin()"
|
||||
@join="tryToJoin"
|
||||
/>
|
||||
|
||||
<GameEndedMenu
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default interface JoinGameObject {
|
||||
failed: boolean;
|
||||
gameCode?: number;
|
||||
playerName: string;
|
||||
gameCode?: string;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class LocalGame {
|
||||
public onGameStateChanged?: (gameState: GameInformationDto | undefined) => void;
|
||||
public onGameEnded?: (gameEndedInfo: GameEnded) => void;
|
||||
|
||||
constructor(settings: GameSettings) {
|
||||
constructor() {
|
||||
this.player1 = new GameConnection();
|
||||
this.player2 = new GameConnection();
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { GameSettings } from '@/scripts/interfaces/GameSettings';
|
||||
import GameConnection, { type GameEnded, type GameInformationDto } from '../signalR/GameConnection';
|
||||
import GameConnection, {
|
||||
type GameEnded,
|
||||
type GameIdentifier,
|
||||
type GameInformationDto,
|
||||
} from '../signalR/GameConnection';
|
||||
import type JoinGameObject from '@/scripts/interfaces/JoinGameObject';
|
||||
|
||||
class OnlineGame {
|
||||
public player: GameConnection;
|
||||
@@ -8,17 +13,51 @@ class OnlineGame {
|
||||
|
||||
public gameState: GameInformationDto | undefined;
|
||||
public onGameCreated?: (gameCode: string) => void;
|
||||
public onGameJoinedFailed?: (gameCode: string) => void;
|
||||
public onGameJoinedFailed?: () => void;
|
||||
public onGameStarted?: () => void;
|
||||
public onGameStateChanged?: (gameState: GameInformationDto | undefined) => void;
|
||||
public onGameEnded?: (gameEndedInfo: GameEnded) => void;
|
||||
|
||||
constructor(settings: GameSettings) {
|
||||
constructor() {
|
||||
this.player = new GameConnection();
|
||||
|
||||
this.player.onGameCreated = (gameIdentifier) => {
|
||||
this.gameId = gameIdentifier.gameId;
|
||||
this.onGameCreated?.(gameIdentifier.gameCode.toString());
|
||||
};
|
||||
|
||||
this.player.onGameJoined = (gameIdentifier: GameIdentifier) => {
|
||||
this.gameId = gameIdentifier.gameId;
|
||||
};
|
||||
|
||||
this.player.onGameStarted = (gameInfo: GameInformationDto) => {
|
||||
console.log("Game started!", gameInfo);
|
||||
this.gameState = gameInfo;
|
||||
this.onGameStarted?.();
|
||||
this.onGameStateChanged?.(this.gameState);
|
||||
|
||||
this.changePlaceDescription();
|
||||
};
|
||||
|
||||
this.player.onFieldUpdated = (currentState: GameInformationDto) => {
|
||||
this.updateState(currentState);
|
||||
this.changePlaceDescription();
|
||||
};
|
||||
|
||||
this.player.onGameEnded = (gameEndedInfo: GameEnded) => {
|
||||
this.onGameEnded?.(gameEndedInfo);
|
||||
};
|
||||
|
||||
this.player.onError = (errorMessage: string) => {
|
||||
switch (errorMessage) {
|
||||
case 'Spiel Existiert nicht!':
|
||||
this.onGameJoinedFailed?.();
|
||||
this.player.disconnect();
|
||||
break;
|
||||
default:
|
||||
console.error('Unhandled error:', errorMessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async createGame(settings: GameSettings) {
|
||||
@@ -26,8 +65,31 @@ class OnlineGame {
|
||||
await this.player.createGame(settings.fieldSize);
|
||||
}
|
||||
|
||||
async drop(index: number){
|
||||
async joinGame(joinObject: JoinGameObject) {
|
||||
if (joinObject.gameCode?.toString().split('').length != 6 || parseInt(joinObject.gameCode) < 0) {
|
||||
this.onGameJoinedFailed?.();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.player.connect(joinObject.playerName);
|
||||
await this.player.joinGame(parseInt(joinObject.gameCode));
|
||||
}
|
||||
|
||||
async updateState(newState: GameInformationDto) {
|
||||
if (this.gameState) {
|
||||
this.gameState = newState;
|
||||
this.onGameStateChanged?.(this.gameState);
|
||||
}
|
||||
}
|
||||
|
||||
async drop(index: number) {
|
||||
await this.player.drop(this.gameId, index);
|
||||
}
|
||||
|
||||
changePlaceDescription() {
|
||||
const playerName =
|
||||
this.gameState?.currentTurn == 1 ? this.gameState.players[0]?.name : this.gameState?.players[1]?.name;
|
||||
this.currentDescription = `${playerName} ist dran mit setzen!`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user