Feat/99 add get gamestate command on server side #263
@@ -154,6 +154,21 @@ public class ServerApp {
|
|||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_list
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_list
|
||||||
.GetLobbyListHandler(responseDispatcher, lobbyManager));
|
.GetLobbyListHandler(responseDispatcher, lobbyManager));
|
||||||
|
|
||||||
|
// 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
|
// GET_LOBBY_STATUS registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
"GET_LOBBY_STATUS",
|
"GET_LOBBY_STATUS",
|
||||||
@@ -169,7 +184,6 @@ public class ServerApp {
|
|||||||
.get_lobby_status.GetLobbyStatusHandler(
|
.get_lobby_status.GetLobbyStatusHandler(
|
||||||
responseDispatcher, lobbyManager, userRegistry));
|
responseDispatcher, lobbyManager, userRegistry));
|
||||||
|
|
||||||
|
|
||||||
// JOIN_LOBBY registration
|
// JOIN_LOBBY registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
"JOIN_LOBBY",
|
"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