Feat/100 add start game command on server side #265
@@ -197,5 +197,19 @@ public class ServerApp {
|
||||
.JoinLobbyRequest>)
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.join_lobby
|
||||
.JoinLobbyHandler(responseDispatcher, lobbyManager, userRegistry));
|
||||
|
||||
// START_GAME registration
|
||||
parserDispatcher.register(
|
||||
"START_GAME",
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||
.StartGameParser());
|
||||
commandRouter.register(
|
||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||
.StartGameRequest.class,
|
||||
(ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler<
|
||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||
.StartGameRequest>)
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||
.StartGameHandler(responseDispatcher, lobbyManager, userRegistry));
|
||||
}
|
||||
}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.checks.UserLoggedInCheck;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.GameController;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.GameEngine;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.RoundManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.TurnManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.Lobby;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyId;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Handler for START_GAME: creates a GameEngine+GameController for the lobby and starts the game.
|
||||
*/
|
||||
public class StartGameHandler extends CommandHandler<StartGameRequest> {
|
||||
private final LobbyManager lobbyManager;
|
||||
private final UserRegistry userRegistry;
|
||||
private static final int DEFAULT_START_CHIPS = 20000;
|
||||
|
||||
public StartGameHandler(
|
||||
ResponseDispatcher responseDispatcher,
|
||||
LobbyManager lobbyManager,
|
||||
UserRegistry userRegistry) {
|
||||
super(responseDispatcher);
|
||||
this.lobbyManager = lobbyManager;
|
||||
this.userRegistry = userRegistry;
|
||||
addCheck(new UserLoggedInCheck(userRegistry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(StartGameRequest request) {
|
||||
Optional<ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User> opt =
|
||||
userRegistry.getBySessionId(request.getSessionId());
|
||||
if (opt.isEmpty()) {
|
||||
// Guard: UserLoggedInCheck should normally handle this
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "USER_NOT_LOGGED_IN", "User not logged in"));
|
||||
return;
|
||||
}
|
||||
|
||||
String username = opt.get().getName();
|
||||
|
||||
// Resolve lobby by provided ID (required)
|
||||
LobbyId lid = LobbyId.of(request.getId());
|
||||
Lobby lobby = lobbyManager.getLobby(lid);
|
||||
if (lobby == null) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(request.getContext(), "LOBBY_NOT_FOUND", "Lobby not found"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure requester is part of the lobby
|
||||
if (!lobby.getPlayerNames().contains(username)) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "NOT_IN_LOBBY", "User not in specified lobby"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent double-start
|
||||
if (lobby.getGameController() != null) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"ALREADY_STARTED",
|
||||
"Game has already been started for this lobby"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Basic player count check
|
||||
if (lobby.getPlayerNames().size() < 2) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(),
|
||||
"NOT_ENOUGH_PLAYERS",
|
||||
"Not enough players to start the game"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create engine + controller
|
||||
GameState state = new GameState();
|
||||
GameEngine engine =
|
||||
new GameEngine(
|
||||
state,
|
||||
new RuleEngine(new ArrayList<>()),
|
||||
new RoundManager(),
|
||||
new TurnManager());
|
||||
GameController game = new GameController(engine);
|
||||
|
||||
// Add all players from lobby
|
||||
for (String playerName : lobby.getPlayerNames()) {
|
||||
game.addPlayer(PlayerId.of(playerName), DEFAULT_START_CHIPS);
|
||||
}
|
||||
|
||||
// Start the game and attach to lobby
|
||||
game.startGame();
|
||||
lobby.initGame(game);
|
||||
|
||||
responseDispatcher.dispatch(
|
||||
new StartGameResponse(
|
||||
request.getContext(), lid.value(), "Game started successfully"));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor;
|
||||
|
||||
/**
|
||||
* Parser for the `START_GAME` command.
|
||||
*
|
||||
* <p>Expected request parameters:
|
||||
*
|
||||
* <ul>
|
||||
* <li>`ID` (int) — numeric lobby identifier (required)
|
||||
* </ul>
|
||||
*
|
||||
* <p>The parser builds a {@link StartGameRequest} containing the parsed lobby id and the original
|
||||
* request context.
|
||||
*/
|
||||
public class StartGameParser implements CommandParser<StartGameRequest> {
|
||||
@Override
|
||||
public StartGameRequest parse(PrimitiveRequest primitiveRequest) {
|
||||
RequestParameterAccessor accessor =
|
||||
new RequestParameterAccessor(primitiveRequest.parameters());
|
||||
int id = accessor.require("ID", Integer::parseInt);
|
||||
return new StartGameRequest(primitiveRequest.context(), id);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
|
||||
/**
|
||||
* Request to start a game in a specific lobby.
|
||||
*
|
||||
* <p>The `START_GAME` request requires the numeric lobby id parameter `ID` identifying the target
|
||||
* lobby. The request carries the usual {@link RequestContext} (session id, request id) via the base
|
||||
* class.
|
||||
*/
|
||||
public class StartGameRequest extends Request {
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* Create a new {@link StartGameRequest}.
|
||||
*
|
||||
* @param context the request context
|
||||
* @param id numeric lobby id to start the game in
|
||||
*/
|
||||
public StartGameRequest(RequestContext context, int id) {
|
||||
super(context);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric lobby id provided by the client.
|
||||
*
|
||||
* @return lobby id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||
|
||||
/**
|
||||
* Success response for {@code START_GAME} containing the started game's identifier and an
|
||||
* informational message.
|
||||
*/
|
||||
public class StartGameResponse extends SuccessResponse {
|
||||
/**
|
||||
* Create a standard start-game response with a default success message.
|
||||
*
|
||||
* @param context the request context
|
||||
* @param gameId numeric id of the started game (uses lobby id)
|
||||
*/
|
||||
public StartGameResponse(RequestContext context, int gameId) {
|
||||
this(context, gameId, "Game started successfully");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a start-game response with a custom message.
|
||||
*
|
||||
* @param context the request context
|
||||
* @param gameId numeric id of the started game (uses lobby id)
|
||||
* @param message informational message for the client
|
||||
*/
|
||||
public StartGameResponse(RequestContext context, int gameId, String message) {
|
||||
super(
|
||||
context,
|
||||
ResponseBody.builder().param("GAME", gameId).param("MESSAGE", message).build());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user