bugfixes in server and cli-client

This commit is contained in:
2021-11-30 11:23:41 +01:00
parent ce739933cb
commit e222ef6ce6
18 changed files with 262 additions and 201 deletions

View File

@@ -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();

View File

@@ -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> {

View File

@@ -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")

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}