diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java
index 7ca3406..e2e6f2a 100644
--- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java
+++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java
@@ -181,6 +181,21 @@ public class ServerApp {
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.bet
.PlayerBetHandler(responseDispatcher, userRegistry, lobbyManager));
+ // RAISE registration
+ parserDispatcher.register(
+ "RAISE",
+ new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise
+ .PlayerRaiseParser());
+ commandRouter.register(
+ ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise.PlayerRaiseRequest
+ .class,
+ (ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler<
+ ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise
+ .PlayerRaiseRequest>)
+ new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise
+ .PlayerRaiseHandler(
+ responseDispatcher, userRegistry, lobbyManager));
+
// GET_LOBBY_STATUS registration
parserDispatcher.register(
"GET_LOBBY_STATUS",
diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseHandler.java
new file mode 100644
index 0000000..4d7b647
--- /dev/null
+++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseHandler.java
@@ -0,0 +1,120 @@
+package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise;
+
+import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.GameController;
+import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
+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.OkResponse;
+import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
+import java.util.Optional;
+
+/**
+ * Handler for the `RAISE` command.
+ *
+ *
+ * This handler validates the request (amount >= 0), resolves the user and
+ * target lobby (by
+ * `GAME_ID` when provided or by session username otherwise), checks that a game
+ * is running and then
+ * forwards the action to the {@code GameController}.
+ *
+ *
+ * On failure the handler dispatches an {@link ErrorResponse} with an
+ * appropriate error code
+ * (e.g. {@code INVALID_AMOUNT}, {@code NOT_IN_LOBBY}, {@code LOBBY_NOT_FOUND},
+ * {@code
+ * GAME_NOT_STARTED}, {@code GAME_ACTION_FAILED}). On success it dispatches an
+ * {@link OkResponse}.
+ */
+public class PlayerRaiseHandler extends CommandHandler {
+ private final UserRegistry userRegistry;
+ private final LobbyManager lobbyManager;
+
+ /**
+ * Creates a new {@code PlayerRaiseHandler}.
+ *
+ * @param responseDispatcher dispatcher used to send responses back to the
+ * client
+ * @param userRegistry registry to resolve users from session ids
+ * @param lobbyManager manager used to lookup lobbies and their game
+ * controllers
+ */
+ public PlayerRaiseHandler(
+ ResponseDispatcher responseDispatcher,
+ UserRegistry userRegistry,
+ LobbyManager lobbyManager) {
+ super(responseDispatcher);
+ this.userRegistry = userRegistry;
+ this.lobbyManager = lobbyManager;
+ }
+
+ /**
+ * Execute the raise request: validate parameters, resolve lobby and game, and
+ * forward the raise
+ * action to the game controller. Sends an {@link ErrorResponse} on failure or
+ * an {@link
+ * OkResponse} on success.
+ *
+ * @param request the parsed {@link PlayerRaiseRequest}
+ */
+ @Override
+ public void execute(PlayerRaiseRequest request) {
+ int amount = request.getAmount();
+
+ if (amount < 0) {
+ responseDispatcher.dispatch(
+ new ErrorResponse(
+ request.getContext(), "INVALID_AMOUNT", "Amount must be non-negative"));
+ return;
+ }
+
+ Optional opt = userRegistry
+ .getBySessionId(request.getSessionId());
+ if (opt.isEmpty()) {
+ responseDispatcher.dispatch(
+ new ErrorResponse(request.getContext(), "NOT_LOGGED_IN", "User not logged in"));
+ return;
+ }
+
+ String username = opt.get().getName();
+
+ Integer gameId = request.getGameId();
+ var lobby = (gameId != null)
+ ? lobbyManager.getLobby(LobbyId.of(gameId))
+ : lobbyManager.getLobbyByUsername(username);
+
+ if (lobby == null) {
+ if (gameId != null) {
+ responseDispatcher.dispatch(
+ new ErrorResponse(
+ request.getContext(), "LOBBY_NOT_FOUND", "Lobby not found"));
+ } else {
+ responseDispatcher.dispatch(
+ new ErrorResponse(
+ request.getContext(), "NOT_IN_LOBBY", "User not in a lobby"));
+ }
+ return;
+ }
+
+ GameController game = lobby.getGameController();
+ if (game == null) {
+ responseDispatcher.dispatch(
+ new ErrorResponse(
+ request.getContext(), "GAME_NOT_STARTED", "Game not started"));
+ return;
+ }
+
+ try {
+ game.playerRaise(PlayerId.of(username), amount);
+ } catch (RuntimeException e) {
+ responseDispatcher.dispatch(
+ new ErrorResponse(request.getContext(), "GAME_ACTION_FAILED", e.getMessage()));
+ return;
+ }
+
+ responseDispatcher.dispatch(new OkResponse(request.getContext()));
+ }
+}
diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseParser.java
new file mode 100644
index 0000000..69aac59
--- /dev/null
+++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseParser.java
@@ -0,0 +1,35 @@
+package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise;
+
+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 `RAISE` command.
+ *
+ *
+ * Parses the request parameters and builds a {@link PlayerRaiseRequest}.
+ * Expected parameters:
+ *
+ *
+ * - `GAME_ID` (optional) - numeric id of the lobby/game to target
+ *
- `AMOUNT` (required) - amount to raise
+ *
+ */
+public class PlayerRaiseParser implements CommandParser {
+ @Override
+ public PlayerRaiseRequest parse(PrimitiveRequest primitiveRequest) {
+ RequestParameterAccessor accessor = new RequestParameterAccessor(primitiveRequest.parameters());
+
+ Integer gameId = null;
+ try {
+ gameId = accessor.optional("GAME_ID", null, Integer::parseInt);
+ } catch (Exception e) {
+ // parse handled by framework
+ }
+
+ int amount = accessor.require("AMOUNT", Integer::parseInt);
+
+ return new PlayerRaiseRequest(primitiveRequest.context(), gameId, amount);
+ }
+}
diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseRequest.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseRequest.java
new file mode 100644
index 0000000..65e5f78
--- /dev/null
+++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/raise/PlayerRaiseRequest.java
@@ -0,0 +1,47 @@
+package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise;
+
+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 for the `RAISE` command.
+ *
+ *
+ * Contains the optional target `gameId` and the `amount` the player wants to
+ * raise.
+ */
+public class PlayerRaiseRequest extends Request {
+ private final Integer gameId;
+ private final int amount;
+
+ /**
+ * Creates a new {@code PlayerRaiseRequest}.
+ *
+ * @param context the request context
+ * @param gameId optional game id of the targeted lobby (may be {@code null})
+ * @param amount the raise amount (non-negative)
+ */
+ public PlayerRaiseRequest(RequestContext context, Integer gameId, int amount) {
+ super(context);
+ this.gameId = gameId;
+ this.amount = amount;
+ }
+
+ /**
+ * Returns the optional target game id.
+ *
+ * @return the game id or {@code null} if not provided
+ */
+ public Integer getGameId() {
+ return gameId;
+ }
+
+ /**
+ * Returns the requested raise amount.
+ *
+ * @return the raise amount
+ */
+ public int getAmount() {
+ return amount;
+ }
+}