Merge branch 'feat/99-add-get-gamestate-command-on-server-side' into 'main'
Feat/99 add get gamestate command on server side Closes #99 See merge request cs108-fs26/Gruppe-13!107
This commit was merged in pull request #263.
This commit is contained in:
@@ -154,7 +154,22 @@ public class ServerApp {
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_list
|
||||
.GetLobbyListHandler(responseDispatcher, lobbyManager));
|
||||
|
||||
// GET_LOBBY_STATUS registration
|
||||
// GET_GAME_STATE registration
|
||||
parserDispatcher.register(
|
||||
"GET_GAME_STATE",
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state
|
||||
.GetGameStateParser());
|
||||
commandRouter.register(
|
||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state
|
||||
.GetGameStateRequest.class,
|
||||
(ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler<
|
||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game
|
||||
.get_game_state.GetGameStateRequest>)
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state
|
||||
.GetGameStateHandler(
|
||||
responseDispatcher, lobbyManager, userRegistry));
|
||||
|
||||
// GET_LOBBY_STATUS registration
|
||||
parserDispatcher.register(
|
||||
"GET_LOBBY_STATUS",
|
||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_status
|
||||
@@ -169,7 +184,6 @@ public class ServerApp {
|
||||
.get_lobby_status.GetLobbyStatusHandler(
|
||||
responseDispatcher, lobbyManager, userRegistry));
|
||||
|
||||
|
||||
// JOIN_LOBBY registration
|
||||
parserDispatcher.register(
|
||||
"JOIN_LOBBY",
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.checks.UserLoggedInCheck;
|
||||
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.User;
|
||||
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;
|
||||
|
||||
/** Handler for GET_GAME_STATE: returns pot, phase, community cards and per-player info. */
|
||||
public class GetGameStateHandler extends CommandHandler<GetGameStateRequest> {
|
||||
private final LobbyManager lobbyManager;
|
||||
private final UserRegistry userRegistry;
|
||||
|
||||
public GetGameStateHandler(
|
||||
ResponseDispatcher responseDispatcher,
|
||||
LobbyManager lobbyManager,
|
||||
UserRegistry userRegistry) {
|
||||
super(responseDispatcher);
|
||||
this.lobbyManager = lobbyManager;
|
||||
this.userRegistry = userRegistry;
|
||||
addCheck(new UserLoggedInCheck(userRegistry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GetGameStateRequest request) {
|
||||
Integer gameId = request.getGameId();
|
||||
String username = request.getUsername();
|
||||
|
||||
Lobby lobby = null;
|
||||
if (gameId != null) {
|
||||
lobby = lobbyManager.getLobby(LobbyId.of(gameId));
|
||||
if (lobby == null) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "LOBBY_NOT_FOUND", "Lobby not found"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (username == null) {
|
||||
var maybeUser = userRegistry.getBySessionId(request.getSessionId());
|
||||
if (maybeUser.isEmpty()) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "NOT_LOGGED_IN", "User not logged in"));
|
||||
return;
|
||||
}
|
||||
User u = maybeUser.get();
|
||||
username = u.getName();
|
||||
}
|
||||
|
||||
lobby = lobbyManager.getLobbyByUsername(username);
|
||||
if (lobby == null) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "NOT_IN_LOBBY", "User not in a lobby"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var game = lobby.getGameController();
|
||||
if (game == null) {
|
||||
responseDispatcher.dispatch(
|
||||
new ErrorResponse(
|
||||
request.getContext(), "GAME_NOT_STARTED", "Game not started"));
|
||||
return;
|
||||
}
|
||||
|
||||
responseDispatcher.dispatch(new GetGameStateResponse(request.getContext(), game, username));
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state;
|
||||
|
||||
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;
|
||||
|
||||
public class GetGameStateParser implements CommandParser<GetGameStateRequest> {
|
||||
@Override
|
||||
public GetGameStateRequest parse(PrimitiveRequest primitiveRequest) {
|
||||
RequestParameterAccessor accessor =
|
||||
new RequestParameterAccessor(primitiveRequest.parameters());
|
||||
Integer gameId = accessor.optional("GAME_ID", null, Integer::parseInt);
|
||||
String username = accessor.optional("USERNAME", null);
|
||||
return new GetGameStateRequest(primitiveRequest.context(), username, gameId);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state;
|
||||
|
||||
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 object for `GET_GAME_STATE`.
|
||||
*
|
||||
* <p>Either `USERNAME` or `GAME_ID` may be provided by the client. If both are omitted the handler
|
||||
* will resolve the username from the session.
|
||||
*/
|
||||
public class GetGameStateRequest extends Request {
|
||||
private final String username;
|
||||
private final Integer gameId;
|
||||
|
||||
public GetGameStateRequest(RequestContext context, String username, Integer gameId) {
|
||||
super(context);
|
||||
this.username = username;
|
||||
this.gameId = gameId;
|
||||
}
|
||||
|
||||
/** Returns the optional username provided in the request. */
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/** Returns the optional game id provided in the request. */
|
||||
public Integer getGameId() {
|
||||
return gameId;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.GameController;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Rank;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
|
||||
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;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder;
|
||||
import java.util.List;
|
||||
|
||||
/** Response carrying a snapshot of the current game state using the project's wire format. */
|
||||
public class GetGameStateResponse extends SuccessResponse {
|
||||
public GetGameStateResponse(
|
||||
RequestContext context, GameController game, String requestingUsername) {
|
||||
super(context, buildBody(game.getState(), requestingUsername));
|
||||
}
|
||||
|
||||
private static ResponseBody buildBody(GameState state, String requestingUsername) {
|
||||
int globalCurrentBet = computeGlobalCurrentBet(state);
|
||||
|
||||
ResponseBodyBuilder builder = ResponseBody.builder();
|
||||
builder.param("PHASE", state.getPhase() == null ? "UNKNOWN" : state.getPhase().name());
|
||||
builder.param("POT", state.getPot().getAmount());
|
||||
builder.param("CURRENT_BET", globalCurrentBet);
|
||||
builder.param("DEALER", state.getDealerIndex());
|
||||
builder.param("ACTIVE_PLAYER", state.getCurrentPlayerIndex());
|
||||
|
||||
appendCommunityCards(builder, state);
|
||||
appendPlayers(builder, state, requestingUsername);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static int computeGlobalCurrentBet(GameState state) {
|
||||
int globalCurrentBet = 0;
|
||||
for (Player p : state.getPlayers()) {
|
||||
int b = state.getCurrentBet(p.getId());
|
||||
if (b > globalCurrentBet) {
|
||||
globalCurrentBet = b;
|
||||
}
|
||||
}
|
||||
return globalCurrentBet;
|
||||
}
|
||||
|
||||
private static void appendCommunityCards(ResponseBodyBuilder builder, GameState state) {
|
||||
for (Card c : state.getCommunityCards()) {
|
||||
builder.block(
|
||||
"CARD",
|
||||
card -> {
|
||||
card.param("VALUE", rankToWire(c.getRank()));
|
||||
card.param("SUIT", suitToWire(c.getSuit()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendPlayers(
|
||||
ResponseBodyBuilder builder, GameState state, String requestingUsername) {
|
||||
for (Player p : state.getPlayers()) {
|
||||
PlayerId pid = p.getId();
|
||||
builder.block(
|
||||
"PLAYER",
|
||||
pblock -> {
|
||||
pblock.param("NAME", pid.value());
|
||||
pblock.param("CHIPS", p.getChips());
|
||||
pblock.param("BET", state.getCurrentBet(pid));
|
||||
pblock.param("STATE", p.isFolded() ? "FOLDED" : "ACTIVE");
|
||||
|
||||
if (requestingUsername != null && requestingUsername.equals(pid.value())) {
|
||||
List<Card> hole = state.getHoleCards(pid);
|
||||
for (Card hc : hole) {
|
||||
pblock.block(
|
||||
"CARD",
|
||||
cblock -> {
|
||||
cblock.param("VALUE", rankToWire(hc.getRank()));
|
||||
cblock.param("SUIT", suitToWire(hc.getSuit()));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static String rankToWire(Rank r) {
|
||||
return switch (r) {
|
||||
case TWO -> "2";
|
||||
case THREE -> "3";
|
||||
case FOUR -> "4";
|
||||
case FIVE -> "5";
|
||||
case SIX -> "6";
|
||||
case SEVEN -> "7";
|
||||
case EIGHT -> "8";
|
||||
case NINE -> "9";
|
||||
case TEN -> "10";
|
||||
case JACK -> "J";
|
||||
case QUEEN -> "Q";
|
||||
case KING -> "K";
|
||||
case ACE -> "A";
|
||||
};
|
||||
}
|
||||
|
||||
private static String suitToWire(Suit s) {
|
||||
return switch (s) {
|
||||
case HEARTS -> "H";
|
||||
case DIAMONDS -> "D";
|
||||
case CLUBS -> "C";
|
||||
case SPADES -> "S";
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user