changes to sync

main
Jonah Bauer 3 years ago
parent 7b7b5a902d
commit 62c9ec2d28

@ -4,6 +4,7 @@ import eu.jonahbauer.wizard.common.machine.TimeoutContext;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.observer.StateMessage; import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.machine.states.TransientState;
import eu.jonahbauer.wizard.core.machine.states.game.Created; import eu.jonahbauer.wizard.core.machine.states.game.Created;
import eu.jonahbauer.wizard.core.machine.states.game.Error; import eu.jonahbauer.wizard.core.machine.states.game.Error;
import eu.jonahbauer.wizard.core.messages.Observer; import eu.jonahbauer.wizard.core.messages.Observer;
@ -57,8 +58,10 @@ public final class Game extends TimeoutContext<GameState, Game> {
@Override @Override
protected void onTransition(GameState from, GameState to) { protected void onTransition(GameState from, GameState to) {
if (!(to instanceof TransientState)) {
notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null")); notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null"));
} }
}
@Override @Override
protected void handleError(Throwable t) { protected void handleError(Throwable t) {

@ -3,12 +3,14 @@ package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.common.machine.TimeoutState; import eu.jonahbauer.wizard.common.machine.TimeoutState;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.machine.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.Unmodifiable;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER; import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS; import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
@ -44,6 +46,14 @@ public abstract class GameState implements TimeoutState<GameState, Game> {
protected final long getSyncTimeout(Game game, boolean absolute) { protected final long getSyncTimeout(Game game, boolean absolute) {
return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().syncTimeout(); return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().syncTimeout();
} }
protected final Optional<GameState> sync(Function<GameData, GameState> state) {
return sync(getData(), state);
}
protected final Optional<GameState> sync(GameData data, Function<GameData, GameState> state) {
return Optional.of(new SyncState(data, state));
}
//</editor-fold> //</editor-fold>
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) { public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {

@ -205,6 +205,11 @@ public final class GameData {
return this; return this;
} }
public GameData keepAll() {
Arrays.fill(required, true);
return this;
}
/** /**
* Returns {@code this} if this map contains a mapping for each of the specified keys or throws a * Returns {@code this} if this map contains a mapping for each of the specified keys or throws a
* {@link NoSuchElementException} otherwise. * {@link NoSuchElementException} otherwise.

@ -11,15 +11,18 @@ import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.SYNC; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.SYNC;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS; import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
public abstract class SyncState extends GameState { public final class SyncState extends GameState implements TransientState {
private final transient Set<UUID> ready = new HashSet<>(); private final transient Set<UUID> ready = new HashSet<>();
private final Function<GameData, GameState> nextState;
public SyncState(GameData data) { public SyncState(GameData data, Function<GameData, GameState> nextState) {
super(data); super(data.keepAll());
this.nextState = nextState;
} }
@Override @Override
@ -34,7 +37,7 @@ public abstract class SyncState extends GameState {
ready.add(player); ready.add(player);
if (ready.size() == get(PLAYERS).size()) { if (ready.size() == get(PLAYERS).size()) {
return Optional.of(getNextState()); return Optional.of(nextState.apply(getData()));
} else { } else {
return Optional.empty(); return Optional.empty();
} }
@ -46,8 +49,6 @@ public abstract class SyncState extends GameState {
@Override @Override
public Optional<GameState> onTimeout(Game game) { public Optional<GameState> onTimeout(Game game) {
game.notify(new TimeoutMessage()); game.notify(new TimeoutMessage());
return Optional.of(getNextState()); return Optional.of(nextState.apply(getData()));
} }
protected abstract GameState getNextState();
} }

@ -0,0 +1,4 @@
package eu.jonahbauer.wizard.core.machine.states;
public interface TransientState {
}

@ -37,11 +37,12 @@ public final class Dealing extends RoundState {
} }
Card trumpCard = deck.draw(); Card trumpCard = deck.draw();
return transition(new DeterminingTrump( return sync(
getData().with( getData().with(
HANDS, Map.copyOf(hands), HANDS, Map.copyOf(hands),
TRUMP_CARD, trumpCard TRUMP_CARD, trumpCard
) ),
)); DeterminingTrump::new
);
} }
} }

@ -103,6 +103,6 @@ public final class DeterminingTrump extends RoundState {
} else { } else {
game.notify(new TrumpMessage(get(TRUMP_CARD), trumpSuit)); game.notify(new TrumpMessage(get(TRUMP_CARD), trumpSuit));
} }
return transition(new TrumpDetermined(data)); return sync(data, Predicting::new);
} }
} }

@ -1,17 +1,19 @@
package eu.jonahbauer.wizard.core.machine.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState;
public final class StartingRound extends SyncState { import java.util.Optional;
public final class StartingRound extends RoundState {
public StartingRound(GameData data) { public StartingRound(GameData data) {
super(RoundState.requirements(data)); super(data);
} }
@Override @Override
protected GameState getNextState() { public Optional<GameState> onEnter(Game context) {
return new Dealing(getData()); return sync(Dealing::new);
} }
} }

@ -1,19 +0,0 @@
package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class TrumpDetermined extends SyncState {
public TrumpDetermined(GameData data) {
super(RoundState.requirements(data).requireEach(PLAYERS, HANDS).require(PREDICTIONS, TRUMP_SUIT, CURRENT_PLAYER));
}
@Override
protected GameState getNextState() {
return new Predicting(getData());
}
}

@ -1,18 +1,21 @@
package eu.jonahbauer.wizard.core.machine.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState; import eu.jonahbauer.wizard.core.machine.states.SyncState;
import eu.jonahbauer.wizard.core.machine.states.round.RoundState; import eu.jonahbauer.wizard.core.machine.states.round.RoundState;
public final class StartingTrick extends SyncState { import java.util.Optional;
public final class StartingTrick extends TrickState {
public StartingTrick(GameData data) { public StartingTrick(GameData data) {
super(RoundState.requirements(TrickState.requirements(data))); super(data);
} }
@Override @Override
protected GameState getNextState() { public Optional<GameState> onEnter(Game context) {
return new PlayingCard(getData()); return sync(PlayingCard::new);
} }
} }

@ -8,6 +8,7 @@ import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.machine.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.MessageQueue; import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.machine.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState;
import eu.jonahbauer.wizard.core.machine.states.game.Finished; import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.model.GameConfiguration; import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations; import eu.jonahbauer.wizard.core.model.Configurations;
@ -38,7 +39,7 @@ public class DeterminingTrumpTest {
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private Game performTest(GameConfiguration configuration, int round, Map<UUID, List<Card>> hands, Card trumpCard, MessageQueue queue) { private Game performTest(GameConfiguration configuration, int round, Map<UUID, List<Card>> hands, Card trumpCard, MessageQueue queue) {
Game game = spy(new Game(configuration, queue)); Game game = spy(new Game(configuration, queue));
doFinish().when(game).transition(any(TrumpDetermined.class)); doFinish().when(game).transition(any(SyncState.class));
queue.setGame(game); queue.setGame(game);
var playerList = List.of(players); var playerList = List.of(players);
@ -76,7 +77,7 @@ public class DeterminingTrumpTest {
InOrder order = inOrder(game); InOrder order = inOrder(game);
order.verify(game).notify(any(StateMessage.class)); // determining trump order.verify(game).notify(any(StateMessage.class)); // determining trump
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.YELLOW_1 && trump.getSuit() == Card.Suit.YELLOW)); order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.YELLOW_1 && trump.getSuit() == Card.Suit.YELLOW));
order.verify(game).transition(any(TrumpDetermined.class)); // round is finished order.verify(game).transition(any(SyncState.class)); // round is finished
order.verify(game).notify(any(StateMessage.class)); // finish order.verify(game).notify(any(StateMessage.class)); // finish
order.verify(game, never()).notify(any()); order.verify(game, never()).notify(any());
order.verify(game, never()).notify(any(), any()); order.verify(game, never()).notify(any(), any());
@ -100,7 +101,7 @@ public class DeterminingTrumpTest {
InOrder order = inOrder(game); InOrder order = inOrder(game);
order.verify(game).notify(any(StateMessage.class)); // determining trump order.verify(game).notify(any(StateMessage.class)); // determining trump
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.GREEN_JESTER && trump.getSuit() == Card.Suit.NONE)); order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.GREEN_JESTER && trump.getSuit() == Card.Suit.NONE));
order.verify(game).transition(any(TrumpDetermined.class)); order.verify(game).transition(any(SyncState.class));
order.verify(game).notify(any(StateMessage.class)); // finish order.verify(game).notify(any(StateMessage.class)); // finish
order.verify(game, never()).notify(any()); order.verify(game, never()).notify(any());
order.verify(game, never()).notify(any(), any()); order.verify(game, never()).notify(any(), any());
@ -127,7 +128,7 @@ public class DeterminingTrumpTest {
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == null)); order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == null));
order.verify(game).notify(any(UserInputMessage.class)); // user input request order.verify(game).notify(any(UserInputMessage.class)); // user input request
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == Card.Suit.GREEN)); order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == Card.Suit.GREEN));
order.verify(game).transition(any(TrumpDetermined.class)); order.verify(game).transition(any(SyncState.class));
order.verify(game).notify(any(StateMessage.class)); // finish order.verify(game).notify(any(StateMessage.class)); // finish
order.verify(game, never()).notify(any()); order.verify(game, never()).notify(any());
order.verify(game, never()).notify(any(), any()); order.verify(game, never()).notify(any(), any());
@ -156,7 +157,7 @@ public class DeterminingTrumpTest {
order.verify(game).notify(any(UserInputMessage.class)); // user input request order.verify(game).notify(any(UserInputMessage.class)); // user input request
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.WEREWOLF && trump.getSuit() == Card.Suit.YELLOW)); order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.WEREWOLF && trump.getSuit() == Card.Suit.YELLOW));
order.verify(game).notify(eq(players[3]), any(HandMessage.class)); // swap trump card and werewolf order.verify(game).notify(eq(players[3]), any(HandMessage.class)); // swap trump card and werewolf
order.verify(game).transition(any(TrumpDetermined.class)); order.verify(game).transition(any(SyncState.class));
order.verify(game).notify(any(StateMessage.class)); // finish order.verify(game).notify(any(StateMessage.class)); // finish
order.verify(game, never()).notify(any()); order.verify(game, never()).notify(any());
order.verify(game, never()).notify(any(), any()); order.verify(game, never()).notify(any(), any());

@ -55,8 +55,9 @@ public class RoundTest {
@Test @Test
public void run_Simple() throws ExecutionException, InterruptedException { public void run_Simple() throws ExecutionException, InterruptedException {
MessageQueue queue = new MessageQueue() MessageQueue queue = new MessageQueue()
.sync(players) .sync(players) // starting_round
.sync(players) .sync(players) // dealing
.sync(players) // determining_trump
.addPrediction(players[3], 3) .addPrediction(players[3], 3)
.addPrediction(players[0], 0) .addPrediction(players[0], 0)
.addPrediction(players[1], 3) .addPrediction(players[1], 3)
@ -114,7 +115,6 @@ public class RoundTest {
order.verify(game, atLeast(4)).notify(any(), any(HandMessage.class)); // hands order.verify(game, atLeast(4)).notify(any(), any(HandMessage.class)); // hands
order.verify(game).notify(any(StateMessage.class)); // determining_trump order.verify(game).notify(any(StateMessage.class)); // determining_trump
order.verify(game).notify(any(TrumpMessage.class)); // trump order.verify(game).notify(any(TrumpMessage.class)); // trump
order.verify(game).notify(any(StateMessage.class)); // trump_determined
for (int i = 0; i < players.length; i++) { for (int i = 0; i < players.length; i++) {
order.verify(game).notify(any(StateMessage.class)); // predicting order.verify(game).notify(any(StateMessage.class)); // predicting
order.verify(game).notify(any(UserInputMessage.class)); // user input order.verify(game).notify(any(UserInputMessage.class)); // user input
@ -148,6 +148,7 @@ public class RoundTest {
@Test @Test
public void run_Anniversary() throws ExecutionException, InterruptedException { public void run_Anniversary() throws ExecutionException, InterruptedException {
MessageQueue queue = new MessageQueue() MessageQueue queue = new MessageQueue()
.sync(players)
.sync(players) .sync(players)
.addPickTrump(players[2], Card.Suit.YELLOW) .addPickTrump(players[2], Card.Suit.YELLOW)
.sync(players) .sync(players)
@ -242,7 +243,6 @@ public class RoundTest {
order.verify(game).notify(any(UserInputMessage.class)); // user input order.verify(game).notify(any(UserInputMessage.class)); // user input
order.verify(game).notify(any(TrumpMessage.class)); // trump order.verify(game).notify(any(TrumpMessage.class)); // trump
order.verify(game).notify(any(), any(HandMessage.class)); // update hand order.verify(game).notify(any(), any(HandMessage.class)); // update hand
order.verify(game).notify(any(StateMessage.class)); // trump_determined
for (int i = 0; i < players.length; i++) { for (int i = 0; i < players.length; i++) {
order.verify(game).notify(any(StateMessage.class)); order.verify(game).notify(any(StateMessage.class));
order.verify(game).notify(any(UserInputMessage.class)); order.verify(game).notify(any(UserInputMessage.class));

Loading…
Cancel
Save