bugfixes in server and cli-client

main
Jonah Bauer 3 years ago
parent ce739933cb
commit e222ef6ce6

@ -86,3 +86,17 @@ object JavaWebSocket {
const val group = "org.java-websocket"
const val id = "$group:Java-WebSocket:$version"
}
object SpringBoot {
const val version = "2.5.6"
const val group = "org.springframework.boot"
const val plugin = group
const val starterWebsocket = "$group:spring-boot-starter-websocket"
const val starterTomcat = "$group:spring-boot-starter-tomcat"
const val starterLog4j2 = "$group:spring-boot-starter-log4j2"
}
object SpringDependencyManagement {
const val version = "1.0.11.RELEASE"
const val plugin = "io.spring.dependency-management"
}

@ -25,7 +25,14 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static picocli.CommandLine.*;
@Command
public class Client extends TimeoutContext<ClientState, Client> implements Runnable {
@Option(names = "-v")
@Getter
private boolean verbose;
private LineReader reader;
@Getter
@ -39,7 +46,7 @@ public class Client extends TimeoutContext<ClientState, Client> implements Runna
private CommandSpec spec;
public static void main(String[] args) {
new Client().run();
new CommandLine(new Client()).execute(args);
}
public Client() {
@ -126,6 +133,9 @@ public class Client extends TimeoutContext<ClientState, Client> implements Runna
}
public void onMessage(ServerMessage message) {
if (verbose) {
println(message.toString());
}
execute(s -> s.onMessage(this, message));
}
@ -239,7 +249,7 @@ public class Client extends TimeoutContext<ClientState, Client> implements Runna
Exception exception = exceptionRef.get();
if (exception == null) {
Object result;
CommandLine.ParseResult parseResult = commandLine.getParseResult();
ParseResult parseResult = commandLine.getParseResult();
if (parseResult.subcommand() != null) {
CommandLine sub = parseResult.subcommand().commandSpec().commandLine();
result = sub.getExecutionResult();

@ -1,15 +1,12 @@
package eu.jonahbauer.wizard.client.cli.commands;
import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.AwaitingAcknowledgement;
import eu.jonahbauer.wizard.client.cli.state.Game;
import eu.jonahbauer.wizard.client.cli.state.Menu;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.player.JuggleMessage;
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.model.Card;
import org.jetbrains.annotations.NotNull;
@ -21,60 +18,41 @@ import static picocli.CommandLine.*;
@Command(name = "\b", subcommands = {HelpCommand.class, QuitCommand.class, ShowCommand.class})
public class GameCommand {
private final Client client;
private final Game game;
public GameCommand(Client client, Game game) {
public GameCommand(Client client) {
this.client = client;
this.game = game;
}
@Command(name = "play")
public AwaitingAcknowledgement play(
public void play(
@Parameters(index = "0", paramLabel = "<card>", completionCandidates = HandCompletion.class) Card card
) {
this.client.send(new InteractionMessage(new PlayCardMessage(card)));
return awaitAcknowledgement();
this.client.waitForReady();
}
@Command(name = "predict")
public AwaitingAcknowledgement predict(
public void predict(
@Parameters(index = "0", paramLabel = "<prediction>") int prediction
) {
this.client.send(new InteractionMessage(new PredictMessage(prediction)));
return awaitAcknowledgement();
this.client.waitForReady();
}
@Command(name = "trump")
public AwaitingAcknowledgement trump(
public void trump(
@Parameters(index = "0", paramLabel = "<suit>") Card.Suit suit
) {
this.client.send(new InteractionMessage(new PickTrumpMessage(suit)));
return awaitAcknowledgement();
this.client.waitForReady();
}
@Command(name = "juggle")
public AwaitingAcknowledgement juggle(
public void juggle(
@Parameters(index = "0", paramLabel = "<card>", completionCandidates = JuggleCompletion.class) Card card
) {
this.client.send(new InteractionMessage(new JuggleMessage(card)));
return awaitAcknowledgement();
}
private AwaitingAcknowledgement awaitAcknowledgement() {
return new AwaitingAcknowledgement(
() -> game,
message -> {
if (message instanceof NackMessage nack) {
int code = nack.getCode();
if (code == NackMessage.ILLEGAL_ARGUMENT || code == NackMessage.ILLEGAL_STATE) {
client.println("Error: " + nack.getMessage());
return game;
}
}
client.println("Fatal: Unexpected message " + message + ". Returning to menu.");
return new Menu();
}
);
this.client.waitForReady();
}
public static class HandCompletion implements Iterable<String> {

@ -1,7 +1,6 @@
package eu.jonahbauer.wizard.client.cli.commands;
import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.AwaitingAcknowledgement;
import eu.jonahbauer.wizard.client.cli.state.AwaitingJoinLobby;
import eu.jonahbauer.wizard.client.cli.state.Session;
import eu.jonahbauer.wizard.common.messages.client.LeaveSessionMessage;
@ -20,13 +19,12 @@ public class SessionCommand {
}
@Command(name = "ready")
public AwaitingAcknowledgement ready(
public void ready(
@Parameters(index = "0", paramLabel = "<ready>", defaultValue = "true") boolean ready
) {
session.setNextReady(ready);
client.send(new ReadyMessage(ready));
return new AwaitingAcknowledgement(
() -> new Session(session.getSelf(), session.getSession(), ready, session.getPlayers())
);
client.waitForReady();
}
@Command(name = "leave", description = "Leaves the current session and returns to the lobby")

@ -1,43 +0,0 @@
package eu.jonahbauer.wizard.client.cli.state;
import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.common.messages.server.AckMessage;
import eu.jonahbauer.wizard.common.messages.server.Response;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public final class AwaitingAcknowledgement extends Awaiting {
private final Supplier<ClientState> success;
private final Function<ServerMessage, ClientState> failure;
public AwaitingAcknowledgement(Supplier<ClientState> success, Function<ServerMessage, ClientState> failure) {
this.success = success;
this.failure = failure;
}
public AwaitingAcknowledgement(Supplier<ClientState> success) {
this.success = success;
this.failure = null;
}
@Override
public Optional<ClientState> onEnter(Client client) {
client.println("Waiting for acknowledgment...");
return super.onEnter(client);
}
@Override
public Optional<ClientState> onMessage(Client client, ServerMessage message) {
if (message instanceof AckMessage) {
return Optional.of(success.get());
} else if (failure != null) {
return Optional.of(failure.apply(message));
} else {
return super.onMessage(client, message);
}
}
}

@ -1,9 +1,7 @@
package eu.jonahbauer.wizard.client.cli.state;
import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.messages.server.SessionJoinedMessage;
import eu.jonahbauer.wizard.common.messages.server.*;
import java.util.Optional;
@ -28,6 +26,9 @@ public final class AwaitingJoinSession extends Awaiting {
default -> { return super.onMessage(client, message); }
}
return Optional.of(new AwaitingJoinLobby());
} else if (message instanceof SessionModifiedMessage || message instanceof SessionRemovedMessage) {
// drop
return Optional.empty();
} else {
return super.onMessage(client, message);
}

@ -31,6 +31,9 @@ public abstract class BaseState implements ClientState {
protected static Optional<ClientState> unexpectedMessage(Client client, ServerMessage message) {
// return to menu on unexpected message
client.println("Fatal: Unexpected message " + message + ". Returning to menu.");
if (client.isVerbose()) {
new Exception().printStackTrace();
}
return Optional.of(new Menu());
}
}

@ -5,7 +5,9 @@ import eu.jonahbauer.wizard.client.cli.commands.GameCommand;
import eu.jonahbauer.wizard.client.cli.util.Pair;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.observer.*;
import eu.jonahbauer.wizard.common.messages.server.AckMessage;
import eu.jonahbauer.wizard.common.messages.server.GameMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.model.Card;
import lombok.Getter;
@ -149,6 +151,18 @@ public final class Game extends BaseState {
throw new AssertionError("Unknown observer message " + observerMessage.getClass().getSimpleName() + "");
}
return Optional.empty();
} else if (message instanceof NackMessage nack) {
int code = nack.getCode();
if (code == NackMessage.ILLEGAL_ARGUMENT || code == NackMessage.ILLEGAL_STATE) {
client.println("Error: " + nack.getMessage());
client.ready();
return Optional.empty();
} else {
return unexpectedMessage(client, message);
}
} else if (message instanceof AckMessage) {
client.ready();
return Optional.empty();
} else {
return unexpectedMessage(client, message);
}

@ -23,7 +23,9 @@ public final class Menu extends BaseState {
@Override
public Optional<ClientState> onMessage(Client client, ServerMessage message) {
throw new AssertionError("Received a ServerMessage while not connected.");
// it is possible that there are messages still queued after
// returning to the menu as a result of a previous message
return Optional.empty();
}
@Override

@ -5,6 +5,7 @@ import eu.jonahbauer.wizard.client.cli.commands.SessionCommand;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.server.*;
import lombok.Getter;
import lombok.Setter;
import picocli.CommandLine.Model.CommandSpec;
import java.util.HashMap;
@ -19,9 +20,12 @@ public final class Session extends BaseState {
private final UUID self;
private final UUID session;
private final boolean ready;
private final Map<UUID, PlayerData> players = new HashMap<>();
private boolean ready;
@Setter
private Boolean nextReady;
public Session(SessionJoinedMessage joined) {
this.self = joined.getPlayer();
this.session = joined.getSession();
@ -68,6 +72,16 @@ public final class Session extends BaseState {
session,
players.values().stream().collect(Collectors.toMap(PlayerData::getUuid, PlayerData::getName))
));
} else if (nextReady != null && message instanceof NackMessage nack) {
client.println("Error: " + nack.getMessage());
nextReady = null;
client.ready();
return Optional.empty();
} else if (nextReady != null && message instanceof AckMessage) {
ready = nextReady;
nextReady = null;
client.ready();
return Optional.empty();
} else {
return unexpectedMessage(client, message);
}

@ -1,15 +1,16 @@
plugins {
id("org.springframework.boot").version("2.5.6")
//id("io.spring.dependency-management").version("1.0.7-RELEASE")
id(SpringBoot.plugin) version SpringBoot.version
id(SpringDependencyManagement.plugin) version SpringDependencyManagement.version
id("java")
}
repositories {
mavenCentral()
id("war")
}
dependencies {
implementation(project(":wizard-core"))
implementation(project(":wizard-common"))
implementation("org.springframework.boot:spring-boot-starter-websocket:2.5.6")
implementation(SpringBoot.starterWebsocket) {
exclude(group = SpringBoot.group, module = "spring-boot-starter-logging")
}
implementation(SpringBoot.starterLog4j2)
providedRuntime(SpringBoot.starterTomcat)
}

@ -9,19 +9,23 @@ import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.server.machine.Player;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
@Getter
@Log4j2
@EqualsAndHashCode(of = "uuid")
public class Session implements Observer {
private static final int MIN_PLAYERS = 3;
@ -46,7 +50,7 @@ public class Session implements Observer {
}
/**
* Associates the given player with this session, removes him from the lobby, notifies all other players in the
* Associates the given player with this session and notifies all other players in the
* session with a {@link PlayerJoinedMessage}, the joining player with a {@link SessionJoinedMessage} and all
* players in the lobby with a {@link SessionModifiedMessage}.
*
@ -55,18 +59,19 @@ public class Session implements Observer {
* @return the players uuid
*/
public synchronized UUID join(Player player, String name) {
if (players.size() == MAX_PLAYERS) {
if (game != null) {
throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started.");
} else if (players.size() == MAX_PLAYERS) {
throw new NackException(NackMessage.SESSION_FULL, "Session is full.");
} else if (players.values().stream().anyMatch(p -> p.getName().equalsIgnoreCase(name))) {
throw new NackException(NackMessage.NAME_TAKEN, "Name is already taken.");
}
Lobby.getInstance().leave(player);
SessionPlayer sessionPlayer;
do {
sessionPlayer = new SessionPlayer(UUID.randomUUID(), name, player);
sessionPlayer = new SessionPlayer(UUID.randomUUID(), name);
} while (players.putIfAbsent(sessionPlayer.getUuid(), sessionPlayer) != null);
sessionPlayer.setPlayer(player);
notifyJoined(sessionPlayer.toData());
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
@ -75,6 +80,7 @@ public class Session implements Observer {
}
public synchronized void leave(UUID player) {
if (game == null) {
if (players.remove(player) != null) {
if (players.size() == 0) {
Lobby.getInstance().removeSession(uuid);
@ -83,46 +89,111 @@ public class Session implements Observer {
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
}
}
} else {
var sessionPlayer = players.get(player);
if (sessionPlayer != null) {
sessionPlayer.setPlayer(null);
if (players.values().stream().noneMatch(SessionPlayer::isConnected)) {
Lobby.getInstance().removeSession(uuid);
}
}
}
}
public synchronized void ready(UUID player, boolean ready) {
var sessionPlayer = players.get(player);
if (sessionPlayer == null) {
public synchronized void ready(UUID uuid, boolean ready) {
var player = players.get(uuid);
if (player == null) {
throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?");
} else if (game != null) {
throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started.");
}
if (sessionPlayer.isReady() != ready) {
sessionPlayer.setReady(ready);
sessionPlayer.getPlayer().send(new AckMessage());
notifyPlayers(new PlayerModifiedMessage(sessionPlayer.toData()));
player.send(new AckMessage());
if (player.isReady() != ready) {
player.setReady(ready);
notifyPlayers(new PlayerModifiedMessage(player.toData()));
}
if (players.size() >= MIN_PLAYERS && players.values().stream().allMatch(SessionPlayer::isReady)) {
game = new Game(Configurations.get(configuration), this);
startGame();
}
}
/**
* Forwards the message sent by the player to the game.
*
* @param uuid the player
* @param message the players message
*/
public synchronized void handlePlayerMessage(UUID uuid, PlayerMessage message) {
var player = players.get(uuid);
if (player == null) {
throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?");
} else if (game == null) {
throw new NackException(NackMessage.GAME_NOT_YET_STARTED, "Game hat not yet started.");
} else {
try {
game.execute(s -> {
// start buffering while game is locked
player.buffer();
return s.onMessage(game, uuid, message);
});
player.send(new AckMessage());
} catch (IllegalStateException e) {
throw new NackException(NackMessage.ILLEGAL_STATE, e.getMessage());
} catch (IllegalArgumentException e) {
throw new NackException(NackMessage.ILLEGAL_ARGUMENT, e.getMessage());
}
}
}
private void startGame() {
notifyPlayers(new StartingGameMessage());
game.start(players.keySet().stream().toList());
game = new Game(Configurations.get(configuration).withTimeout(timeout), this);
game.start(List.copyOf(players.keySet()));
CompletableFuture.runAsync(() -> {
while (true) {
try {
game.await();
break;
} catch (InterruptedException e) {
// ignored
} catch (ExecutionException e) {
log.warn("Game completed with exception.", e);
break;
}
}
players.forEach((id, player) -> player.setReady(false));
synchronized (this) {
game = null;
for (SessionPlayer player : List.copyOf(players.values())) {
if (!player.isConnected()) {
leave(player.getUuid());
}
}
}
});
}
private void notifyJoined(PlayerData joined) {
var message = new PlayerJoinedMessage(joined);
for (SessionPlayer player : players.values()) {
if (player.getUuid().equals(joined.getUuid())) {
player.getPlayer().send(new SessionJoinedMessage(
player.send(new SessionJoinedMessage(
getUuid(),
player.getUuid(),
players.values().stream().map(SessionPlayer::toData).toList(),
player.getSecret()
));
} else {
player.getPlayer().send(message);
player.send(message);
}
}
}
private void notifyPlayers(ServerMessage message) {
for (SessionPlayer player : players.values()) {
player.getPlayer().send(message);
player.send(message);
}
}
@ -130,16 +201,6 @@ public class Session implements Observer {
return new SessionData(uuid, name, players.size(), configuration);
}
/**
* Forwards the message sent by the player to the game.
*
* @param player the player
* @param message the players message
*/
public void handlePlayerMessage(UUID player, PlayerMessage message) {
game.onMessage(player, message);
}
@Override
public void notify(ObserverMessage message) {
notifyPlayers(new GameMessage(message));
@ -147,7 +208,7 @@ public class Session implements Observer {
@Override
public void notify(UUID player, ObserverMessage message) {
players.get(player).getPlayer().send(new GameMessage(message));
players.get(player).send(new GameMessage(message));
}
@Data
@ -155,8 +216,8 @@ public class Session implements Observer {
private static class SessionPlayer {
private final UUID uuid;
private final String name;
private final Player player;
private final String secret = generateSecret();
private Player player;
private boolean ready;
private static String generateSecret() {
@ -169,5 +230,21 @@ public class Session implements Observer {
public PlayerData toData() {
return new PlayerData(uuid, name, ready);
}
public void send(ServerMessage message) {
if (player != null) {
player.send(message);
}
}
public void buffer() {
if (player != null) {
player.buffer();
}
}
public boolean isConnected() {
return player != null;
}
}
}

@ -3,18 +3,18 @@ package eu.jonahbauer.wizard.server.machine;
import eu.jonahbauer.wizard.common.machine.State;
import eu.jonahbauer.wizard.common.messages.client.ClientMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.server.NackException;
import org.springframework.web.socket.CloseStatus;
import java.util.Optional;
public interface ClientState extends State<ClientState, Player> {
default Optional<ClientState> onOpen(Player player) {
throw new IllegalStateException(); // TODO nachdenken
throw new IllegalStateException();
}
default Optional<ClientState> onMessage(Player player, ClientMessage message) {
player.send(new NackMessage(NackMessage.BAD_REQUEST, "Unexpected message."));
return Optional.empty();
throw new NackException(NackMessage.UNEXPECTED_MESSAGE, "Don't know what to do with " + message + ".");
}
Optional<ClientState> onClose(Player player, CloseStatus status);

@ -2,17 +2,20 @@ package eu.jonahbauer.wizard.server.machine;
import eu.jonahbauer.wizard.common.machine.Context;
import eu.jonahbauer.wizard.common.messages.client.ClientMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.Response;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.server.NackException;
import eu.jonahbauer.wizard.server.machine.states.CreatedState;
import lombok.SneakyThrows;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.util.ArrayList;
public class Player extends Context<ClientState, Player> {
private final WebSocketSession session;
private final ArrayList<ServerMessage> buffer = new ArrayList<>();
private Class<?> shouldBuffer = null;
public Player(WebSocketSession session) {
super(new CreatedState());
@ -29,22 +32,32 @@ public class Player extends Context<ClientState, Player> {
}
public void onMessage(ClientMessage message) {
try {
execute(s -> s.onMessage(this, message));
} catch (NackException e) {
send(new NackMessage(e.getCode(), e.getMessage()));
}
}
public void onClose(CloseStatus status) {
execute(s -> s.onClose(this, status));
}
public void buffer() {
shouldBuffer = Response.class;
}
@SneakyThrows
public void send(ServerMessage message) {
synchronized (session) {
if (shouldBuffer != null && shouldBuffer.isInstance(message)) {
session.sendMessage(new TextMessage(message.toString()));
shouldBuffer = null;
for (ServerMessage serverMessage : buffer) {
session.sendMessage(new TextMessage(serverMessage.toString()));
}
buffer.clear();
} else if (shouldBuffer != null) {
buffer.add(message);
} else {
session.sendMessage(new TextMessage(message.toString()));
}
}
}
}

@ -1,46 +0,0 @@
package eu.jonahbauer.wizard.server.machine.states;
import eu.jonahbauer.wizard.common.messages.client.ClientMessage;
import eu.jonahbauer.wizard.common.messages.client.LeaveSessionMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.StartingGameMessage;
import eu.jonahbauer.wizard.server.machine.ClientState;
import eu.jonahbauer.wizard.server.machine.Player;
import org.springframework.web.socket.CloseStatus;
import java.util.Optional;
public class InGame implements ClientState {
@Override
public Optional<ClientState> onEnter(Player context) {
context.send(new StartingGameMessage());
return ClientState.super.onEnter(context);
}
@Override
public void onExit(Player context) {
ClientState.super.onExit(context);
}
@Override
public Optional<ClientState> onOpen(Player player) {
return Optional.empty();
}
@Override
public Optional<ClientState> onMessage(Player player, ClientMessage message) {
if(message instanceof LeaveSessionMessage) {
//?
return Optional.empty();
} else {
player.send(new NackMessage(0, "Error: Invalid Message!"));
return Optional.empty();
}
}
@Override
public Optional<ClientState> onClose(Player player, CloseStatus status) {
return Optional.empty();
}
}

@ -23,10 +23,21 @@ public class LobbyState implements ClientState {
public Optional<ClientState> onMessage(Player player, ClientMessage message) {
if (message instanceof CreateSessionMessage create) {
Lobby.getInstance().leave(player);
try {
player.buffer();
var session = Lobby.getInstance().createSession(create);
var uuid = session.join(player, create.getPlayerName());
return Optional.of(new SessionState(session.getUuid(), uuid, create.getPlayerName()));
} catch (NackException nack) {
// nack must be sent before joinlobby
player.send(nack.toMessage());
Lobby.getInstance().join(player);
return Optional.empty();
}
} else if (message instanceof JoinSessionMessage join) {
Lobby.getInstance().leave(player);
try {
player.buffer();
var session = Lobby.getInstance().getSession(join.getSession());
if (session == null) {
throw new NackException(NackMessage.NOT_FOUND, "Session not found.");
@ -34,6 +45,12 @@ public class LobbyState implements ClientState {
var uuid = session.join(player, join.getPlayerName());
return Optional.of(new SessionState(session.getUuid(), uuid, join.getPlayerName()));
}
} catch (NackException nack) {
// nack must be sent before joinlobby
player.send(nack.toMessage());
Lobby.getInstance().join(player);
return Optional.empty();
}
} else {
return ClientState.super.onMessage(player, message);
}

@ -4,7 +4,6 @@ import eu.jonahbauer.wizard.common.messages.client.ClientMessage;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.client.LeaveSessionMessage;
import eu.jonahbauer.wizard.common.messages.client.ReadyMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.server.Lobby;
import eu.jonahbauer.wizard.server.machine.ClientState;
import eu.jonahbauer.wizard.server.machine.Player;
@ -42,8 +41,7 @@ public class SessionState implements ClientState {
Lobby.getInstance().getSession(session).handlePlayerMessage(self, playerMessage);
return Optional.empty();
} else {
player.send(new NackMessage(NackMessage.UNEXPECTED_MESSAGE, "Invalid Message!"));
return Optional.empty();
return ClientState.super.onMessage(player, message);
}
}

@ -1,6 +1,9 @@
package eu.jonahbauer.wizard.server.socket;
import com.google.gson.JsonParseException;
import eu.jonahbauer.wizard.common.messages.client.ClientMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.server.NackException;
import eu.jonahbauer.wizard.server.machine.Player;
import lombok.extern.log4j.Log4j2;
import org.jetbrains.annotations.NotNull;
@ -29,7 +32,14 @@ public class WizardSocketHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(@NotNull WebSocketSession session, @NotNull TextMessage text) {
players.get(session.getId()).onMessage(ClientMessage.parse(text.getPayload()));
var player = players.get(session.getId());
try {
player.onMessage(ClientMessage.parse(text.getPayload()));
} catch (NackException e) {
player.send(e.toMessage());
} catch (JsonParseException e) {
player.send(new NackMessage(NackMessage.MALFORMED_MESSAGE, "Could not parse " + text.getPayload() + "."));
}
}
@Override

Loading…
Cancel
Save