Compare commits

..

No commits in common. 'dev' and 'main' have entirely different histories.
dev ... main

@ -2,8 +2,8 @@ package eu.jonahbauer.wizard.client.cli.commands;
import eu.jonahbauer.wizard.client.cli.Client; import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.Game; import eu.jonahbauer.wizard.client.cli.state.Game;
import eu.jonahbauer.wizard.client.cli.util.Pair;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;

@ -2,6 +2,7 @@ package eu.jonahbauer.wizard.client.cli.state;
import eu.jonahbauer.wizard.client.cli.Client; import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.commands.GameCommand; import eu.jonahbauer.wizard.client.cli.commands.GameCommand;
import eu.jonahbauer.wizard.client.cli.util.Pair;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage; import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.observer.*; import eu.jonahbauer.wizard.common.messages.observer.*;
@ -11,7 +12,6 @@ import eu.jonahbauer.wizard.common.messages.server.GameMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage; import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage; import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair;
import lombok.Getter; import lombok.Getter;
import java.time.Instant; import java.time.Instant;

@ -0,0 +1,24 @@
package eu.jonahbauer.wizard.client.cli.util;
import java.util.Map;
public record Pair<F,S>(F first, S second) implements Map.Entry<F, S> {
public static <F,S> Pair<F,S> of(F first, S second) {
return new Pair<>(first, second);
}
@Override
public F getKey() {
return first();
}
@Override
public S getValue() {
return second();
}
@Override
public S setValue(S value) {
throw new UnsupportedOperationException();
}
}

@ -1,5 +1,6 @@
package eu.jonahbauer.wizard.client.cli.util; package eu.jonahbauer.wizard.client.cli.util;
import eu.jonahbauer.wizard.client.cli.Client; import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.ClientState; import eu.jonahbauer.wizard.client.cli.state.ClientState;
import picocli.CommandLine; import picocli.CommandLine;

@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import eu.jonahbauer.wizard.client.libgdx.screens.MainMenuScreen; import eu.jonahbauer.wizard.client.libgdx.screens.MainMenuScreen;
import eu.jonahbauer.wizard.client.libgdx.util.SavedData; import eu.jonahbauer.wizard.client.libgdx.util.SavedData;
import eu.jonahbauer.wizard.client.libgdx.util.SoundManager;
import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager; import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager;
import lombok.Getter; import lombok.Getter;
@ -20,8 +19,6 @@ public class WizardGame extends Game {
public SpriteBatch batch; public SpriteBatch batch;
public WizardAssetManager assets; public WizardAssetManager assets;
public SoundManager sounds;
public final SavedData storage = new SavedData(); public final SavedData storage = new SavedData();
private boolean fullscreenToggle; private boolean fullscreenToggle;
@ -38,8 +35,6 @@ public class WizardGame extends Game {
assets.loadShared(); assets.loadShared();
assets.finishLoading(); assets.finishLoading();
sounds = new SoundManager(assets);
// background music // background music
Music backgroundMusic = assets.get(WizardAssetManager.MUSIC_BACKGROUND, Music.class); Music backgroundMusic = assets.get(WizardAssetManager.MUSIC_BACKGROUND, Music.class);
backgroundMusic.setLooping(true); backgroundMusic.setLooping(true);
@ -82,7 +77,6 @@ public class WizardGame extends Game {
@Override @Override
public void dispose () { public void dispose () {
batch.dispose(); batch.dispose();
sounds.dispose();
assets.dispose(); assets.dispose();
client.shutdownNow(); client.shutdownNow();
var socket = client.getSocket(); var socket = client.getSocket();

@ -1,53 +0,0 @@
package eu.jonahbauer.wizard.client.libgdx.actors;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.scenes.scene2d.ui.List;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.Align;
import lombok.Getter;
import lombok.Setter;
public abstract class IconList<T> extends List<T> {
@Getter
@Setter
private float iconWidth = -1;
@Getter
@Setter
private float iconPadding = 8;
@SuppressWarnings("unused")
public IconList(Skin skin) {
super(skin);
}
@SuppressWarnings("unused")
public IconList(Skin skin, String styleName) {
super(skin, styleName);
}
@SuppressWarnings("unused")
public IconList(ListStyle style) {
super(style);
}
public abstract Drawable getIcon(T item);
@Override
@Deprecated
public void setAlignment(int alignment) {}
@Override
protected GlyphLayout drawItem(Batch batch, BitmapFont font, int index, T item, float x, float y, float width) {
var text = toString(item);
var icon = getIcon(item);
var height = font.getCapHeight();
var iconWidth = this.iconWidth < 0 ? height : this.iconWidth;
icon.draw(batch, x, y - height, iconWidth, height);
return font.draw(batch, text, x + iconWidth + iconPadding, y, 0, text.length(), width - iconWidth - iconPadding, Align.left, false, "...");
}
}

@ -1,21 +0,0 @@
package eu.jonahbauer.wizard.client.libgdx.listeners;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import eu.jonahbauer.wizard.client.libgdx.util.SoundManager;
public class ButtonClickListener extends ChangeListener {
private final SoundManager sounds;
public ButtonClickListener(SoundManager sounds) {
this.sounds = sounds;
}
@Override
public void changed(ChangeEvent event, Actor actor) {
if (actor instanceof Button) {
sounds.sfxClick();
}
}
}

@ -29,6 +29,7 @@ public class ConnectScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(Menu.class, Menu::showMenuScreen); game.getClient().execute(Menu.class, Menu::showMenuScreen);
sfxClick();
} else if (actor == buttonConnect) { } else if (actor == buttonConnect) {
try { try {
var uriString = ConnectScreen.this.uriField.getText(); var uriString = ConnectScreen.this.uriField.getText();
@ -38,6 +39,8 @@ public class ConnectScreen extends MenuScreen {
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
uriField.setStyle(getTextFieldErrorStyle()); uriField.setStyle(getTextFieldErrorStyle());
} }
sfxClick();
} }
} }
}; };

@ -31,8 +31,10 @@ public class CreateGameScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::showListScreen); game.getClient().execute(Lobby.class, Lobby::showListScreen);
sfxClick();
} else if (actor == buttonContinue) { } else if (actor == buttonContinue) {
create(); create();
sfxClick();
} }
} }
}; };

@ -18,6 +18,7 @@ public class ErrorScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(BaseState.class, BaseState::dismissErrorScreen); game.getClient().execute(BaseState.class, BaseState::dismissErrorScreen);
sfxClick();
} }
} }
}; };

@ -22,7 +22,10 @@ import eu.jonahbauer.wizard.client.libgdx.actors.CardsGroup;
import eu.jonahbauer.wizard.client.libgdx.actors.PadOfTruth; import eu.jonahbauer.wizard.client.libgdx.actors.PadOfTruth;
import eu.jonahbauer.wizard.client.libgdx.state.Game; import eu.jonahbauer.wizard.client.libgdx.state.Game;
import eu.jonahbauer.wizard.client.libgdx.state.Session; import eu.jonahbauer.wizard.client.libgdx.state.Session;
import eu.jonahbauer.wizard.client.libgdx.util.*; import eu.jonahbauer.wizard.client.libgdx.util.AnimationTimings;
import eu.jonahbauer.wizard.client.libgdx.util.CardUtil;
import eu.jonahbauer.wizard.client.libgdx.util.Pair;
import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import lombok.Getter; import lombok.Getter;
@ -418,13 +421,6 @@ public class GameScreen extends WizardScreen {
execute(new StartRoundOverlay(this, round)); execute(new StartRoundOverlay(this, round));
} }
public void deal() {
execute(parallel(
run(() -> game.sounds.sfxShuffle()),
delay(SoundManager.CARD_SHUFFLE_DURATION)
));
}
public void startTrick() { public void startTrick() {
clearActivePlayer(); clearActivePlayer();
execute(() -> cardStack.clearChildren()); execute(() -> cardStack.clearChildren());
@ -535,8 +531,6 @@ public class GameScreen extends WizardScreen {
cardStack.add(seat, actor); cardStack.add(seat, actor);
sequence.addAction(delay(actor)); sequence.addAction(delay(actor));
sequence.addAction(delay(AnimationTimings.STACK_HOLD)); sequence.addAction(delay(AnimationTimings.STACK_HOLD));
game.sounds.sfxPlayCard();
})); }));
execute(sequence); execute(sequence);
} }

@ -31,12 +31,15 @@ public class InstructionScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(Menu.class, Menu::showMenuScreen); game.getClient().execute(Menu.class, Menu::showMenuScreen);
sfxClick();
} else if (actor == nextPageButton) { } else if (actor == nextPageButton) {
currentPage = MathUtils.clamp(currentPage + 1, 0, MAX_PAGE); currentPage = MathUtils.clamp(currentPage + 1, 0, MAX_PAGE);
showPage(currentPage); showPage(currentPage);
sfxClick();
} else if (actor == previousPageButton) { } else if (actor == previousPageButton) {
currentPage = MathUtils.clamp(currentPage - 1, 0, MAX_PAGE); currentPage = MathUtils.clamp(currentPage - 1, 0, MAX_PAGE);
showPage(currentPage); showPage(currentPage);
sfxClick();
} }
} }
}; };

@ -1,5 +1,6 @@
package eu.jonahbauer.wizard.client.libgdx.screens; package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup; import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.WizardGame;

@ -3,10 +3,7 @@ package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.IconList;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager; import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener; import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener;
import eu.jonahbauer.wizard.client.libgdx.state.Lobby; import eu.jonahbauer.wizard.client.libgdx.state.Lobby;
@ -20,6 +17,7 @@ public class LobbyScreen extends MenuScreen {
private TextButton buttonBack; private TextButton buttonBack;
private TextButton buttonJoin; private TextButton buttonJoin;
private TextButton buttonRejoin;
private TextButton buttonCreate; private TextButton buttonCreate;
private TextField playerName; private TextField playerName;
@ -28,7 +26,6 @@ public class LobbyScreen extends MenuScreen {
private Label labelSessionConfiguration; private Label labelSessionConfiguration;
private UUID selectedSession; private UUID selectedSession;
private boolean rejoin = false;
private List<SessionData> sessions; private List<SessionData> sessions;
private ScrollPane sessionListContainer; private ScrollPane sessionListContainer;
@ -37,14 +34,16 @@ public class LobbyScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::disconnect); game.getClient().execute(Lobby.class, Lobby::disconnect);
sfxClick();
} else if (actor == buttonJoin) { } else if (actor == buttonJoin) {
if (rejoin) { join();
game.getClient().execute(Lobby.class, Lobby::showRejoinScreen); sfxClick();
} else {
join();
}
} else if (actor == buttonCreate) { } else if (actor == buttonCreate) {
game.getClient().execute(Lobby.class, Lobby::showCreateScreen); game.getClient().execute(Lobby.class, Lobby::showCreateScreen);
sfxClick();
} else if (actor == buttonRejoin) {
game.getClient().execute(Lobby.class, Lobby::showRejoinScreen);
sfxClick();
} }
} }
}; };
@ -65,26 +64,21 @@ public class LobbyScreen extends MenuScreen {
buttonCreate.addListener(listener); buttonCreate.addListener(listener);
getButtonGroup().addActor(buttonCreate); getButtonGroup().addActor(buttonCreate);
buttonRejoin = new TextButton(messages.get("menu.lobby.rejoin"), skin);
buttonRejoin.addListener(listener);
getButtonGroup().addActor(buttonRejoin);
buttonJoin = new TextButton(messages.get("menu.lobby.join"), skin); buttonJoin = new TextButton(messages.get("menu.lobby.join"), skin);
buttonJoin.addListener(listener); buttonJoin.addListener(listener);
getButtonGroup().addActor(buttonJoin); getButtonGroup().addActor(buttonJoin);
getButtonGroup().setWidth(0.55f * WizardGame.WIDTH); getButtonGroup().setWidth(0.55f * WizardGame.WIDTH);
sessions = new IconList<>(skin) { sessions = new List<>(skin) {
// TODO better icons
private final Drawable running = skin.getDrawable(UiskinAtlas.NOT_READY);
private final Drawable notRunning = skin.getDrawable(UiskinAtlas.READY);
@Override @Override
public String toString(SessionData session) { public String toString(SessionData session) {
return session.getName(); return session.getName();
} }
@Override
public Drawable getIcon(SessionData item) {
return item.isRunning() ? running : notRunning;
}
}; };
sessions.addListener(new ChangeListener() { sessions.addListener(new ChangeListener() {
@Override @Override
@ -108,11 +102,12 @@ public class LobbyScreen extends MenuScreen {
stage.addActor(content); stage.addActor(content);
stage.addCaptureListener(new KeyboardFocusManager( stage.addCaptureListener(new KeyboardFocusManager(
sessions, playerName, buttonBack, buttonCreate, buttonJoin sessions, playerName, buttonBack, buttonCreate, buttonRejoin, buttonJoin
)); ));
buttonBack.setName("button_back"); buttonBack.setName("button_back");
buttonJoin.setName("button_join"); buttonJoin.setName("button_join");
buttonJoin.setName("button_rejoin");
buttonCreate.setName("button_create"); buttonCreate.setName("button_create");
sessions.setName("session_list"); sessions.setName("session_list");
playerName.setName("player_name"); playerName.setName("player_name");
@ -199,21 +194,14 @@ public class LobbyScreen extends MenuScreen {
labelSessionPlayerCount.setText(Integer.toString(data.getPlayerCount())); labelSessionPlayerCount.setText(Integer.toString(data.getPlayerCount()));
labelSessionConfiguration.setText(data.getConfiguration().toString()); labelSessionConfiguration.setText(data.getConfiguration().toString());
selectedSession = data.getUuid(); selectedSession = data.getUuid();
updateRejoin(data.isRunning());
} else { } else {
labelSessionName.setText(""); labelSessionName.setText("");
labelSessionPlayerCount.setText(""); labelSessionPlayerCount.setText("");
labelSessionConfiguration.setText(""); labelSessionConfiguration.setText("");
selectedSession = null; selectedSession = null;
updateRejoin(false);
} }
} }
private void updateRejoin(boolean rejoin) {
this.rejoin = rejoin;
buttonJoin.setText(messages.get("menu.lobby." + (rejoin ? "rejoin" : "join")));
}
private void join() { private void join() {
boolean error = false; boolean error = false;

@ -23,10 +23,13 @@ public class MainMenuScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonPlay) { if (actor == buttonPlay) {
game.getClient().execute(Menu.class, Menu::showConnectScreen); game.getClient().execute(Menu.class, Menu::showConnectScreen);
sfxClick();
} else if (actor == buttonQuit) { } else if (actor == buttonQuit) {
sfxClick();
Gdx.app.exit(); Gdx.app.exit();
} else if (actor == buttonInstruction) { } else if (actor == buttonInstruction) {
game.getClient().execute(Menu.class, Menu::showInstructionScreen); game.getClient().execute(Menu.class, Menu::showInstructionScreen);
sfxClick();
} }
} }
}; };

@ -26,8 +26,10 @@ public class RejoinScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) { if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::showListScreen); game.getClient().execute(Lobby.class, Lobby::showListScreen);
sfxClick();
} else if (actor == buttonContinue) { } else if (actor == buttonContinue) {
rejoin(); rejoin();
sfxClick();
} }
} }
}; };

@ -1,12 +1,15 @@
package eu.jonahbauer.wizard.client.libgdx.screens; package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.utils.Align;
import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas; import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.IconList;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager; import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.state.Session; import eu.jonahbauer.wizard.client.libgdx.state.Session;
import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.PlayerData;
@ -14,7 +17,7 @@ import eu.jonahbauer.wizard.common.model.Configuration;
import java.util.UUID; import java.util.UUID;
public class SessionScreen extends MenuScreen { public class WaitingScreen extends MenuScreen {
private TextButton buttonLeave; private TextButton buttonLeave;
private TextButton buttonReady; private TextButton buttonReady;
@ -31,13 +34,15 @@ public class SessionScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonLeave) { if (actor == buttonLeave) {
game.getClient().execute(Session.class, Session::leave); game.getClient().execute(Session.class, Session::leave);
sfxClick();
} else if (actor == buttonReady) { } else if (actor == buttonReady) {
game.getClient().execute(Session.class, Session::toggleReady); game.getClient().execute(Session.class, Session::toggleReady);
sfxClick();
} }
} }
}; };
public SessionScreen(WizardGame game) { public WaitingScreen(WizardGame game) {
super(game); super(game);
} }
@ -45,19 +50,19 @@ public class SessionScreen extends MenuScreen {
public void show() { public void show() {
super.show(); super.show();
buttonLeave = new TextButton(messages.get("menu.session.leave"), skin); buttonLeave = new TextButton(messages.get("menu.waiting.leave"), skin);
buttonLeave.addListener(listener); buttonLeave.addListener(listener);
getButtonGroup().addActor(buttonLeave); getButtonGroup().addActor(buttonLeave);
buttonReady = new TextButton(messages.get("menu.session.ready"), skin); buttonReady = new TextButton(messages.get("menu.waiting.ready"), skin);
buttonReady.addListener(listener); buttonReady.addListener(listener);
getButtonGroup().addActor(buttonReady); getButtonGroup().addActor(buttonReady);
getButtonGroup().setWidth(0.55f * WizardGame.WIDTH); getButtonGroup().setWidth(0.55f * WizardGame.WIDTH);
players = new IconList<>(skin) { players = new List<>(skin) {
private final Drawable ready = skin.getDrawable(UiskinAtlas.READY); private final TextureRegion ready = skin.getRegion(UiskinAtlas.READY);
private final Drawable notReady = skin.getDrawable(UiskinAtlas.NOT_READY); private final TextureRegion notReady = skin.getRegion(UiskinAtlas.NOT_READY);
@Override @Override
public String toString(PlayerData player) { public String toString(PlayerData player) {
@ -65,8 +70,16 @@ public class SessionScreen extends MenuScreen {
} }
@Override @Override
public Drawable getIcon(PlayerData item) { @SuppressWarnings("SuspiciousNameCombination")
return item.isReady() ? ready : notReady; protected GlyphLayout drawItem(Batch batch, BitmapFont font, int index, PlayerData item, float x, float y, float width) {
String string = toString(item);
var height = font.getCapHeight();
if (item.isReady()) {
batch.draw(ready, x, y - height, height, height);
} else {
batch.draw(notReady, x, y - height, height, height);
}
return font.draw(batch, string, x + height + 8, y, 0, string.length(), width - height - 8, Align.left, false, "...");
} }
}; };
@ -108,13 +121,13 @@ public class SessionScreen extends MenuScreen {
infoTable.columnDefaults(0).growX().width(infoTableWidth); infoTable.columnDefaults(0).growX().width(infoTableWidth);
infoTable.setSize(infoTableWidth, 400); infoTable.setSize(infoTableWidth, 400);
infoTable.add(messages.get("menu.session.session_name.label")).row(); infoTable.add(messages.get("menu.waiting.session_name.label")).row();
infoTable.add(labelSessionName).row(); infoTable.add(labelSessionName).row();
infoTable.add(messages.get("menu.session.session_uuid.label")).row(); infoTable.add(messages.get("menu.waiting.session_uuid.label")).row();
infoTable.add(labelSessionUUID).row(); infoTable.add(labelSessionUUID).row();
infoTable.add(messages.get("menu.session.session_configuration.label")).row(); infoTable.add(messages.get("menu.waiting.session_configuration.label")).row();
infoTable.add(labelSessionConfiguration).row(); infoTable.add(labelSessionConfiguration).row();
infoTable.add(messages.get("menu.session.player_name.label")).row(); infoTable.add(messages.get("menu.waiting.player_name.label")).row();
infoTable.add(labelPlayerName).row(); infoTable.add(labelPlayerName).row();
return infoTable; return infoTable;
@ -125,7 +138,7 @@ public class SessionScreen extends MenuScreen {
} }
public void setReady(boolean ready) { public void setReady(boolean ready) {
buttonReady.setText(messages.get(ready ? "menu.session.not_ready" : "menu.session.ready")); buttonReady.setText(messages.get(ready ? "menu.waiting.not_ready" : "menu.waiting.ready"));
} }
public void addPlayer(PlayerData player) { public void addPlayer(PlayerData player) {

@ -2,6 +2,7 @@ package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen; import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
@ -15,7 +16,6 @@ import com.badlogic.gdx.utils.viewport.Viewport;
import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas; import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame; import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.listeners.AutoFocusListener; import eu.jonahbauer.wizard.client.libgdx.listeners.AutoFocusListener;
import eu.jonahbauer.wizard.client.libgdx.listeners.ButtonClickListener;
import eu.jonahbauer.wizard.client.libgdx.listeners.ButtonKeyListener; import eu.jonahbauer.wizard.client.libgdx.listeners.ButtonKeyListener;
import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager; import eu.jonahbauer.wizard.client.libgdx.util.WizardAssetManager;
import org.jetbrains.annotations.MustBeInvokedByOverriders; import org.jetbrains.annotations.MustBeInvokedByOverriders;
@ -31,6 +31,7 @@ public abstract class WizardScreen implements Screen {
protected Viewport viewport; protected Viewport viewport;
private Image background; private Image background;
private Sound sfxClick;
protected float offsetX; protected float offsetX;
protected float offsetY; protected float offsetY;
@ -55,10 +56,11 @@ public abstract class WizardScreen implements Screen {
stage = new Stage(viewport); stage = new Stage(viewport);
stage.addListener(new ButtonKeyListener()); stage.addListener(new ButtonKeyListener());
stage.addListener(new AutoFocusListener()); stage.addListener(new AutoFocusListener());
stage.addListener(new ButtonClickListener(game.sounds));
stage.setDebugAll(WizardGame.DEBUG); stage.setDebugAll(WizardGame.DEBUG);
Gdx.input.setInputProcessor(stage); Gdx.input.setInputProcessor(stage);
sfxClick = assets.get(WizardAssetManager.SFX_CLICK);
} }
@Override @Override
@ -108,4 +110,8 @@ public abstract class WizardScreen implements Screen {
public void dispose() { public void dispose() {
stage.dispose(); stage.dispose();
} }
protected void sfxClick() {
sfxClick.play(0.6f);
}
} }

@ -68,7 +68,7 @@ public final class AwaitingJoinSession extends Awaiting {
)); ));
} else { } else {
return Optional.of(new Session( return Optional.of(new Session(
session, sessionName, configuration, new SessionData(session, sessionName, -1, configuration),
players, players,
player player
)); ));

@ -6,6 +6,7 @@ import eu.jonahbauer.wizard.client.libgdx.screens.GameScreen;
import eu.jonahbauer.wizard.client.libgdx.util.Pair; import eu.jonahbauer.wizard.client.libgdx.util.Pair;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage; import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
import eu.jonahbauer.wizard.common.messages.observer.*; import eu.jonahbauer.wizard.common.messages.observer.*;
import eu.jonahbauer.wizard.common.messages.player.*; import eu.jonahbauer.wizard.common.messages.player.*;
import eu.jonahbauer.wizard.common.messages.server.AckMessage; import eu.jonahbauer.wizard.common.messages.server.AckMessage;
@ -117,7 +118,6 @@ public final class Game extends BaseState {
case "starting_round" -> { case "starting_round" -> {
return onStartRound(client); return onStartRound(client);
} }
case "dealing" -> onDealing();
case "starting_trick" -> onStartTrick(); case "starting_trick" -> onStartTrick();
case "juggling" -> onJuggle(); case "juggling" -> onJuggle();
case "finishing_round" -> onFinishingRound(); case "finishing_round" -> onFinishingRound();
@ -190,10 +190,6 @@ public final class Game extends BaseState {
return Optional.empty(); return Optional.empty();
} }
private void onDealing() {
if (gameScreen != null) gameScreen.deal();
}
private void onStartTrick() { private void onStartTrick() {
log.info("Trick {} is starting...", trick + 1); log.info("Trick {} is starting...", trick + 1);
trick ++; trick ++;
@ -437,7 +433,7 @@ public final class Game extends BaseState {
private Optional<ClientState> returnToSession() { private Optional<ClientState> returnToSession() {
return Optional.of(new Session( return Optional.of(new Session(
session, sessionName, configuration, new SessionData(session, sessionName, -1, configuration),
players.entrySet().stream() players.entrySet().stream()
.map(entry -> new PlayerData(entry.getKey(), entry.getValue(), false)) .map(entry -> new PlayerData(entry.getKey(), entry.getValue(), false))
.toList(), .toList(),

@ -1,10 +1,11 @@
package eu.jonahbauer.wizard.client.libgdx.state; package eu.jonahbauer.wizard.client.libgdx.state;
import eu.jonahbauer.wizard.client.libgdx.Client; import eu.jonahbauer.wizard.client.libgdx.Client;
import eu.jonahbauer.wizard.client.libgdx.screens.SessionScreen; import eu.jonahbauer.wizard.client.libgdx.screens.WaitingScreen;
import eu.jonahbauer.wizard.common.messages.client.LeaveSessionMessage; import eu.jonahbauer.wizard.common.messages.client.LeaveSessionMessage;
import eu.jonahbauer.wizard.common.messages.client.ReadyMessage; import eu.jonahbauer.wizard.common.messages.client.ReadyMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
import eu.jonahbauer.wizard.common.messages.server.*; import eu.jonahbauer.wizard.common.messages.server.*;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.common.model.Configuration;
import lombok.Getter; import lombok.Getter;
@ -15,7 +16,7 @@ import java.util.*;
@Log4j2 @Log4j2
@Getter @Getter
public final class Session extends BaseState { public final class Session extends BaseState {
private SessionScreen sessionScreen; private WaitingScreen sessionScreen;
private final UUID self; private final UUID self;
@ -27,14 +28,14 @@ public final class Session extends BaseState {
private boolean sending; private boolean sending;
public Session(UUID session, String name, Configuration configuration, Collection<PlayerData> players, UUID self) { public Session(SessionData session, Collection<PlayerData> players, UUID self) {
this(session, name, configuration, players, self, false); this(session, players, self, false);
} }
public Session(UUID session, String name, Configuration configuration, Collection<PlayerData> players, UUID self, boolean dontSwitchScreen) { public Session(SessionData session, Collection<PlayerData> players, UUID self, boolean dontSwitchScreen) {
this.session = session; this.session = session.getUuid();
this.sessionName = name; this.sessionName = session.getName();
this.configuration = configuration; this.configuration = session.getConfiguration();
players.forEach(p -> this.players.put(p.getUuid(), p)); players.forEach(p -> this.players.put(p.getUuid(), p));
this.dontSwitchScreen = dontSwitchScreen; this.dontSwitchScreen = dontSwitchScreen;
@ -121,7 +122,7 @@ public final class Session extends BaseState {
} }
public Optional<ClientState> showInfoScreen(Client client) { public Optional<ClientState> showInfoScreen(Client client) {
sessionScreen = new SessionScreen(client.getGame()); sessionScreen = new WaitingScreen(client.getGame());
client.getGame().setScreen(sessionScreen); client.getGame().setScreen(sessionScreen);
sessionScreen.setPlayers(players.values().toArray(new PlayerData[0])); sessionScreen.setPlayers(players.values().toArray(new PlayerData[0]));
sessionScreen.setReady(players.get(self).isReady()); sessionScreen.setReady(players.get(self).isReady());

@ -1,47 +0,0 @@
package eu.jonahbauer.wizard.client.libgdx.util;
import com.badlogic.gdx.audio.Sound;
import lombok.Setter;
public class SoundManager {
public static final float CARD_SHUFFLE_DURATION = 4.2f;
private final WizardAssetManager assets;
@Setter
private float sfxVolume = 1;
private final Sound click;
private final Sound cardPlayed;
private final Sound cardShuffle;
public SoundManager(WizardAssetManager assets) {
this.assets = assets;
assets.load(WizardAssetManager.SFX_CLICK, Sound.class);
assets.load(WizardAssetManager.SFX_CARD_PLAYED, Sound.class);
assets.load(WizardAssetManager.SFX_CARD_SHUFFLE, Sound.class);
assets.finishLoading();
this.click = assets.get(WizardAssetManager.SFX_CLICK);
this.cardPlayed = assets.get(WizardAssetManager.SFX_CARD_PLAYED);
this.cardShuffle = assets.get(WizardAssetManager.SFX_CARD_SHUFFLE);
}
public void sfxClick() {
this.click.play(sfxVolume);
}
public void sfxShuffle() {
this.cardShuffle.play(sfxVolume);
}
public void sfxPlayCard() {
this.cardPlayed.play(sfxVolume);
}
public void dispose() {
assets.unload(WizardAssetManager.SFX_CLICK);
assets.unload(WizardAssetManager.SFX_CARD_PLAYED);
assets.unload(WizardAssetManager.SFX_CARD_SHUFFLE);
}
}

@ -4,6 +4,7 @@ import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.I18NBundleLoader; import com.badlogic.gdx.assets.loaders.I18NBundleLoader;
import com.badlogic.gdx.assets.loaders.SkinLoader; import com.badlogic.gdx.assets.loaders.SkinLoader;
import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Skin;
@ -19,9 +20,7 @@ public class WizardAssetManager {
public static final String ATLAS_SKIN = UiskinAtlas.$PATH; public static final String ATLAS_SKIN = UiskinAtlas.$PATH;
public static final String ATLAS_GAME = GameAtlas.$PATH; public static final String ATLAS_GAME = GameAtlas.$PATH;
public static final String SFX_CLICK = "sound/button_click.mp3"; public static final String SFX_CLICK = "button_click_s.mp3";
public static final String SFX_CARD_PLAYED = "sound/card_played.mp3";
public static final String SFX_CARD_SHUFFLE = "sound/card_shuffle.mp3";
public static final String MUSIC_BACKGROUND = "background.mp3"; public static final String MUSIC_BACKGROUND = "background.mp3";
public static final String CURSOR = "cursor.png"; public static final String CURSOR = "cursor.png";
@ -36,6 +35,7 @@ public class WizardAssetManager {
manager.load(SKIN, Skin.class, new SkinLoader.SkinParameter(ATLAS_SKIN)); manager.load(SKIN, Skin.class, new SkinLoader.SkinParameter(ATLAS_SKIN));
manager.load(MUSIC_BACKGROUND, Music.class); manager.load(MUSIC_BACKGROUND, Music.class);
manager.load(SFX_CLICK, Sound.class);
} }
public void loadGame() { public void loadGame() {

@ -150,14 +150,14 @@ menu.loading.joining_session=Joining session...
menu.loading.joining_lobby=Joining lobby... menu.loading.joining_lobby=Joining lobby...
menu.loading.back=Return To Main Menu menu.loading.back=Return To Main Menu
menu.session.ready=Ready menu.waiting.ready=Ready
menu.session.not_ready=Not Ready menu.waiting.not_ready=Not Ready
menu.session.leave=Leave menu.waiting.leave=Leave
menu.session.player_name.label=Own Name menu.waiting.player_name.label=Own Name
menu.session.session_name.label=Session Name menu.waiting.session_name.label=Session Name
menu.session.session_uuid.label=Session UUID menu.waiting.session_uuid.label=Session UUID
menu.session.session_configuration.label=Configuration menu.waiting.session_configuration.label=Configuration
menu.error.malformed_message=Error: Malformed Message menu.error.malformed_message=Error: Malformed Message
menu.error.unexpected_message=Error: Unexpected Message menu.error.unexpected_message=Error: Unexpected Message

@ -143,14 +143,14 @@ menu.loading.joining_session=Trete Sitzung bei...
menu.loading.joining_lobby=Trete Warteraum bei... menu.loading.joining_lobby=Trete Warteraum bei...
menu.loading.back=Zurück zum Hauptmenü menu.loading.back=Zurück zum Hauptmenü
menu.session.ready=Bereit menu.waiting.ready=Bereit
menu.session.not_ready=Nicht Bereit menu.waiting.not_ready=Nicht Bereit
menu.session.leave=Verlassen menu.waiting.leave=Verlassen
menu.session.player_name.label=Eigener Name menu.waiting.player_name.label=Eigener Name
menu.session.session_name.label=Sitzungsname menu.waiting.session_name.label=Sitzungsname
menu.session.session_uuid.label=Sitzungs-ID menu.waiting.session_uuid.label=Sitzungs-ID
menu.session.session_configuration.label=Spielvariante menu.waiting.session_configuration.label=Spielvariante
menu.error.malformed_message=Fehler: Missgebildete Nachricht menu.error.malformed_message=Fehler: Missgebildete Nachricht
menu.error.unexpected_message=Fehler: Unerwartete Nachricht menu.error.unexpected_message=Fehler: Unerwartete Nachricht

@ -3,7 +3,7 @@ package eu.jonahbauer.wizard.common.messages.client;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.jonahbauer.wizard.common.messages.ParseException; import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.util.SerializationUtil; import eu.jonahbauer.wizard.common.util.SerializationUtil;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.SneakyThrows; import lombok.SneakyThrows;

@ -26,8 +26,4 @@ public class SessionData {
* Configuration of the session * Configuration of the session
*/ */
private final @NonNull Configuration configuration; private final @NonNull Configuration configuration;
/**
* Whether the session is running.
*/
private final boolean running;
} }

@ -2,7 +2,7 @@ package eu.jonahbauer.wizard.common.messages.observer;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.jonahbauer.wizard.common.messages.ParseException; import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.messages.util.SerializationUtil; import eu.jonahbauer.wizard.common.util.SerializationUtil;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.SneakyThrows; import lombok.SneakyThrows;

@ -2,7 +2,7 @@ package eu.jonahbauer.wizard.common.messages.player;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.jonahbauer.wizard.common.messages.ParseException; import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.messages.util.SerializationUtil; import eu.jonahbauer.wizard.common.util.SerializationUtil;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.SneakyThrows; import lombok.SneakyThrows;

@ -3,7 +3,7 @@ package eu.jonahbauer.wizard.common.messages.server;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.jonahbauer.wizard.common.messages.ParseException; import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.util.SerializationUtil; import eu.jonahbauer.wizard.common.util.SerializationUtil;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.SneakyThrows; import lombok.SneakyThrows;

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.common.messages.util; package eu.jonahbauer.wizard.common.util;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
@ -10,10 +10,10 @@ import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import eu.jonahbauer.wizard.common.messages.ParseException; import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.util.StringUtil;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Locale;
@UtilityClass @UtilityClass
public class SerializationUtil { public class SerializationUtil {
@ -61,7 +61,7 @@ public class SerializationUtil {
if (Modifier.isFinal(modifiers) || subclass.isSealed() && !Modifier.isAbstract(modifiers)) { if (Modifier.isFinal(modifiers) || subclass.isSealed() && !Modifier.isAbstract(modifiers)) {
var name = subclass.getSimpleName(); var name = subclass.getSimpleName();
if (name.endsWith(suffix)) name = name.substring(0, name.length() - suffix.length()); if (name.endsWith(suffix)) name = name.substring(0, name.length() - suffix.length());
name = StringUtil.toSnakeCase(name); name = name.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase(Locale.ROOT);
objectMapper.registerSubtypes(new NamedType(subclass, name)); objectMapper.registerSubtypes(new NamedType(subclass, name));
} }

@ -1,23 +0,0 @@
module eu.jonahbauer.wizard.common {
exports eu.jonahbauer.wizard.common.machine;
exports eu.jonahbauer.wizard.common.messages;
exports eu.jonahbauer.wizard.common.messages.client;
exports eu.jonahbauer.wizard.common.messages.data;
exports eu.jonahbauer.wizard.common.messages.observer;
exports eu.jonahbauer.wizard.common.messages.player;
exports eu.jonahbauer.wizard.common.messages.server;
exports eu.jonahbauer.wizard.common.model;
exports eu.jonahbauer.wizard.common.util;
opens eu.jonahbauer.wizard.common.messages.client to com.fasterxml.jackson.databind;
opens eu.jonahbauer.wizard.common.messages.data to com.fasterxml.jackson.databind;
opens eu.jonahbauer.wizard.common.messages.observer to com.fasterxml.jackson.databind;
opens eu.jonahbauer.wizard.common.messages.player to com.fasterxml.jackson.databind;
opens eu.jonahbauer.wizard.common.messages.server to com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.module.paramnames;
requires static lombok;
requires static org.jetbrains.annotations;
}

@ -0,0 +1,72 @@
package eu.jonahbauer.wizard.core;
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.model.Card;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import java.util.List;
import java.util.Scanner;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CLI {
public static void main(String[] args) {
GameConfiguration config = Configurations.DEFAULT;
Observer observer = (player, msg) -> System.out.println(msg);
Game game = new Game(config, observer);
var players = List.of(
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID()
);
game.start(players);
Scanner scanner = new Scanner(System.in);
Pattern pattern = Pattern.compile("(\\d) ([a-z]+) (.*)");
while (scanner.hasNextLine()) {
try {
Matcher matcher = pattern.matcher(scanner.nextLine());
if (!matcher.find()) {
System.err.println("Format is \"(\\d) ([a-z]+) (.*)\"");
continue;
}
String player = matcher.group(1);
String command = matcher.group(2);
String param = matcher.group(3);
int id = Integer.parseInt(player);
if (id > players.size()) {
System.err.println("ID must be between 0 and " + (players.size() - 1));
continue;
}
switch (command) {
case "predict" -> {
int prediction = Integer.parseInt(param);
game.onMessage(players.get(id), new PredictMessage(prediction));
}
case "play" -> {
Card card = Card.valueOf(param);
game.onMessage(players.get(id), new PlayCardMessage(card));
}
case "trump" -> {
Card.Suit suit = Card.Suit.valueOf(param);
game.onMessage(players.get(id), new PickTrumpMessage(suit));
}
default -> System.err.println("Unknown command: " + command);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}

@ -1,22 +1,18 @@
package eu.jonahbauer.wizard.core; package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.common.machine.TimeoutContext; 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.common.model.Configuration; import eu.jonahbauer.wizard.core.machine.states.TransientState;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.states.game.Created;
import eu.jonahbauer.wizard.core.states.TransientState; import eu.jonahbauer.wizard.core.machine.states.game.Error;
import eu.jonahbauer.wizard.core.states.game.Created; import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.core.states.game.Error;
import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.model.GameConfiguration; import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.common.util.StringUtil; import eu.jonahbauer.wizard.core.util.Util;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Blocking; import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@ -34,25 +30,20 @@ public final class Game extends TimeoutContext<GameState, Game> {
private final CompletableFuture<Void> future = new CompletableFuture<>(); private final CompletableFuture<Void> future = new CompletableFuture<>();
private Game(Random random, Configuration config, long timeout, long syncTimeout, Observer observer) { public Game(GameConfiguration config, Observer observer) {
super(new Created()); super(new Created());
this.random = random; this.random = new Random();
this.config = Configurations.get(config).withTimeout(timeout).withSyncTimeout(syncTimeout); this.config = config;
this.observer = observer; this.observer = observer;
} }
public Game(Configuration config, long timeout, long syncTimeout, Observer observer) { public Game(long seed, GameConfiguration config, Observer observer) {
this(new Random(), config, timeout, syncTimeout, observer); super(new Created());
} this.random = new Random(seed);
this.config = config;
@TestOnly this.observer = observer;
public Game(long seed, Configuration config, long timeout, long syncTimeout, Observer observer) {
this(new Random(seed), config, timeout, syncTimeout, observer);
} }
@TestOnly
@VisibleForTesting
@SuppressWarnings("ClassEscapesDefinedScope")
public void resume(GameState state) { public void resume(GameState state) {
transition(new Created(), state); transition(new Created(), state);
} }
@ -65,17 +56,10 @@ public final class Game extends TimeoutContext<GameState, Game> {
execute(s -> s.onMessage(this, player, message)); execute(s -> s.onMessage(this, player, message));
} }
public void onMessage(UUID player, PlayerMessage message, Runnable preHandleMessage) {
execute(s -> {
preHandleMessage.run();
return s.onMessage(this, player, message);
});
}
@Override @Override
protected void onTransition(GameState from, GameState to) { protected void onTransition(GameState from, GameState to) {
if (!(to instanceof TransientState)) { if (!(to instanceof TransientState)) {
notify(new StateMessage(to != null ? StringUtil.toSnakeCase(to.getClass().getSimpleName()) : "null")); notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null"));
} }
} }

@ -1,9 +1,10 @@
package eu.jonahbauer.wizard.core.states; 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.ContinueMessage; import eu.jonahbauer.wizard.common.messages.player.ContinueMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.Game; 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;
@ -12,8 +13,8 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import static eu.jonahbauer.wizard.core.states.GameData.CURRENT_PLAYER; import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
import static eu.jonahbauer.wizard.core.states.GameData.PLAYERS; import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
@Unmodifiable @Unmodifiable
public abstract class GameState implements TimeoutState<GameState, Game> { public abstract class GameState implements TimeoutState<GameState, Game> {

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.states; package eu.jonahbauer.wizard.core.machine.states;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.core.states; package eu.jonahbauer.wizard.core.machine.states;
public class InvalidDataException extends RuntimeException { public class InvalidDataException extends RuntimeException {
public InvalidDataException(String message) { public InvalidDataException(String message) {

@ -1,10 +1,11 @@
package eu.jonahbauer.wizard.core.states; package eu.jonahbauer.wizard.core.machine.states;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.ContinueMessage; import eu.jonahbauer.wizard.common.messages.player.ContinueMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
@ -13,7 +14,7 @@ import java.util.UUID;
import java.util.function.Function; 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.states.GameData.PLAYERS; import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
public final class SyncState extends GameState implements TransientState { public final class SyncState extends GameState implements TransientState {
private final transient Set<UUID> ready = new HashSet<>(); private final transient Set<UUID> ready = new HashSet<>();

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

@ -1,8 +1,8 @@
package eu.jonahbauer.wizard.core.states.game; package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.states.game; package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import lombok.Getter; import lombok.Getter;
@Getter @Getter

@ -1,8 +1,8 @@
package eu.jonahbauer.wizard.core.states.game; package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.Optional; import java.util.Optional;

@ -1,13 +1,13 @@
package eu.jonahbauer.wizard.core.states.game; package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage; import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
import java.util.Optional; import java.util.Optional;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class Finishing extends GameState { public final class Finishing extends GameState {

@ -0,0 +1,22 @@
package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.round.StartingRound;
import java.util.Optional;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
public final class Starting extends GameState {
public Starting(GameData data) {
super(data.require(PLAYERS));
}
@Override
public Optional<GameState> onEnter(Game game) {
return transition(new StartingRound(getData()));
}
}

@ -1,14 +1,14 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage; import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage; import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.InvalidDataException; import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -16,7 +16,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.CHANGE_PREDICTION; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.CHANGE_PREDICTION;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class ChangingPrediction extends RoundState { public final class ChangingPrediction extends RoundState {
private transient final int oldPrediction; private transient final int oldPrediction;

@ -1,15 +1,15 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage; import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.model.deck.Deck; import eu.jonahbauer.wizard.core.model.deck.Deck;
import java.util.*; import java.util.*;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class Dealing extends RoundState { public final class Dealing extends RoundState {

@ -1,15 +1,15 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage; import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.model.card.GameCards; import eu.jonahbauer.wizard.core.model.card.GameCards;
import java.util.*; import java.util.*;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class DeterminingTrump extends RoundState { public final class DeterminingTrump extends RoundState {

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage; import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
@ -7,17 +7,17 @@ import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage; import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.TransientState; import eu.jonahbauer.wizard.core.machine.states.TransientState;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.PICK_TRUMP; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.PICK_TRUMP;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TRUMP_CARD; import static eu.jonahbauer.wizard.core.machine.states.GameData.TRUMP_CARD;
public final class DeterminingTrumpUserInput extends RoundState implements TransientState { public final class DeterminingTrumpUserInput extends RoundState implements TransientState {
private final transient UUID player; private final transient UUID player;

@ -1,9 +1,9 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.game.Finishing; import eu.jonahbauer.wizard.core.machine.states.game.Finishing;
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage; import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
import java.util.HashMap; import java.util.HashMap;
@ -11,7 +11,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class FinishingRound extends RoundState { public final class FinishingRound extends RoundState {

@ -1,10 +1,10 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.trick.StartingTrick; import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage; import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
@ -16,7 +16,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.MAKE_PREDICTION; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.MAKE_PREDICTION;
@Getter @Getter

@ -1,11 +1,11 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public abstract class RoundState extends GameState { public abstract class RoundState extends GameState {
public static GameData requirements(GameData data) { public static GameData requirements(GameData data) {

@ -1,8 +1,8 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import java.util.Optional; import java.util.Optional;

@ -1,19 +1,19 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.InvalidDataException; import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import eu.jonahbauer.wizard.core.states.round.ChangingPrediction; import eu.jonahbauer.wizard.core.machine.states.round.ChangingPrediction;
import eu.jonahbauer.wizard.core.states.round.FinishingRound; import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.common.messages.observer.TrickMessage; import eu.jonahbauer.wizard.common.messages.observer.TrickMessage;
import eu.jonahbauer.wizard.core.model.card.*; import eu.jonahbauer.wizard.core.model.card.*;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import java.util.*; import java.util.*;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class FinishingTrick extends TrickState { public final class FinishingTrick extends TrickState {

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage; import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
@ -6,15 +6,15 @@ import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.JuggleMessage; import eu.jonahbauer.wizard.common.messages.player.JuggleMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.*; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.*;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class Juggling extends TrickState { public final class Juggling extends TrickState {
private final transient Map<UUID, Card> juggledCards = new ConcurrentHashMap<>(); private final transient Map<UUID, Card> juggledCards = new ConcurrentHashMap<>();

@ -1,22 +1,22 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage; import eu.jonahbauer.wizard.common.messages.observer.TimeoutMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.InvalidDataException; import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import eu.jonahbauer.wizard.common.messages.observer.CardMessage; import eu.jonahbauer.wizard.common.messages.observer.CardMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage; import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.model.card.GameCards; import eu.jonahbauer.wizard.core.model.card.GameCards;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.PLAY_CARD; import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.PLAY_CARD;
public final class PlayingCard extends TrickState { public final class PlayingCard extends TrickState {

@ -1,8 +1,8 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import java.util.Optional; import java.util.Optional;

@ -1,9 +1,9 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.round.RoundState; import eu.jonahbauer.wizard.core.machine.states.round.RoundState;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public abstract class TrickState extends RoundState { public abstract class TrickState extends RoundState {
public static GameData requirements(GameData data) { public static GameData requirements(GameData data) {

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.core; package eu.jonahbauer.wizard.core.messages;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.util.Comparator; import java.util.Comparator;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.Getter; import lombok.Getter;
import java.util.List; import java.util.List;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.Getter; import lombok.Getter;
import java.util.List; import java.util.List;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import lombok.Getter; import lombok.Getter;
import java.util.List; import java.util.List;

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

@ -1,22 +0,0 @@
package eu.jonahbauer.wizard.core.states.game;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.round.StartingRound;
import java.util.Optional;
import static eu.jonahbauer.wizard.core.states.GameData.PLAYERS;
public final class Starting extends GameState {
public Starting(GameData data) {
super(data.require(PLAYERS));
}
@Override
public Optional<GameState> onEnter(Game game) {
return transition(new StartingRound(getData()));
}
}

@ -1,4 +1,4 @@
package eu.jonahbauer.wizard.common.util; package eu.jonahbauer.wizard.core.util;
import java.util.Map; import java.util.Map;

@ -1,11 +1,11 @@
package eu.jonahbauer.wizard.common.util; package eu.jonahbauer.wizard.core.util;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.util.Locale; import java.util.Locale;
@UtilityClass @UtilityClass
public class StringUtil { public class Util {
public static String toSnakeCase(String str) { public static String toSnakeCase(String str) {
return str.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase(Locale.ROOT); return str.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase(Locale.ROOT);
} }

@ -1,8 +0,0 @@
module eu.jonahbauer.wizard.core {
exports eu.jonahbauer.wizard.core;
requires eu.jonahbauer.wizard.common;
requires static lombok;
requires static org.jetbrains.annotations;
}

@ -1,7 +1,6 @@
package eu.jonahbauer.wizard.core; package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.Game;
import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo; import org.junit.jupiter.api.RepetitionInfo;
@ -14,7 +13,7 @@ public class GameTest {
public void runDefault(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException { public void runDefault(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game( Game game = new Game(
repetitionInfo.getCurrentRepetition(), repetitionInfo.getCurrentRepetition(),
Configuration.DEFAULT, 0, 0, Configurations.DEFAULT.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg) (player, msg) -> System.out.println(msg)
); );
var players = List.of( var players = List.of(
@ -32,7 +31,7 @@ public class GameTest {
public void runAnniversary2016(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException { public void runAnniversary2016(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game( Game game = new Game(
repetitionInfo.getCurrentRepetition(), repetitionInfo.getCurrentRepetition(),
Configuration.ANNIVERSARY_2016, 0, 0, Configurations.ANNIVERSARY_2016.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg) (player, msg) -> System.out.println(msg)
); );
var players = List.of( var players = List.of(
@ -50,7 +49,7 @@ public class GameTest {
public void runAnniversary2021(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException { public void runAnniversary2021(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game( Game game = new Game(
repetitionInfo.getCurrentRepetition(), repetitionInfo.getCurrentRepetition(),
Configuration.ANNIVERSARY_2021, 0, 0, Configurations.ANNIVERSARY_2021.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg) (player, msg) -> System.out.println(msg)
); );
var players = List.of( var players = List.of(

@ -1,6 +1,6 @@
package eu.jonahbauer.wizard.core; package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.core.states.game.Finished; import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;

@ -1,11 +1,11 @@
package eu.jonahbauer.wizard.core; package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.*; import eu.jonahbauer.wizard.common.messages.player.*;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState; import eu.jonahbauer.wizard.core.messages.Observer;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;

@ -1,5 +1,6 @@
package eu.jonahbauer.wizard.core.states; package eu.jonahbauer.wizard.core.machine.states;
import eu.jonahbauer.wizard.core.machine.GameState;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.HashSet; import java.util.HashSet;

@ -1,16 +1,17 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage; import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.StateMessage; import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage; import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.MessageQueue; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.SyncState;
import eu.jonahbauer.wizard.core.states.SyncState; import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.states.game.Finished; import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@ -19,9 +20,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish; import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry; import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -35,8 +36,8 @@ public class DeterminingTrumpTest {
@SneakyThrows @SneakyThrows
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private Game performTest(Configuration 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, 10 * 60 * 1000, 10 * 60 * 1000, queue)); Game game = spy(new Game(configuration, queue));
doFinish().when(game).transition(argThat(argument -> argument instanceof SyncState && argument.getData().has(TRUMP_SUIT))); doFinish().when(game).transition(argThat(argument -> argument instanceof SyncState && argument.getData().has(TRUMP_SUIT)));
queue.setGame(game); queue.setGame(game);
@ -69,7 +70,7 @@ public class DeterminingTrumpTest {
// play cards in given order // play cards in given order
MessageQueue queue = new MessageQueue(); MessageQueue queue = new MessageQueue();
Game game = performTest(Configuration.ANNIVERSARY_2021, 0, hands, Card.YELLOW_1, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 0, hands, Card.YELLOW_1, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -94,7 +95,7 @@ public class DeterminingTrumpTest {
MessageQueue queue = new MessageQueue() MessageQueue queue = new MessageQueue()
.sync(players); .sync(players);
Game game = performTest(Configuration.ANNIVERSARY_2021, 0, hands, Card.GREEN_JESTER, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 0, hands, Card.GREEN_JESTER, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -120,7 +121,7 @@ public class DeterminingTrumpTest {
.sync(players) .sync(players)
.addPickTrump(players[0], Card.Suit.GREEN); .addPickTrump(players[0], Card.Suit.GREEN);
Game game = performTest(Configuration.ANNIVERSARY_2021, 0, hands, Card.BLUE_WIZARD, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 0, hands, Card.BLUE_WIZARD, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -148,7 +149,7 @@ public class DeterminingTrumpTest {
.sync(players) .sync(players)
.addPickTrump(players[3], Card.Suit.YELLOW); .addPickTrump(players[3], Card.Suit.YELLOW);
Game game = performTest(Configuration.ANNIVERSARY_2021, 0, hands, Card.GREEN_1, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 0, hands, Card.GREEN_1, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);

@ -1,16 +1,17 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage; import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
import eu.jonahbauer.wizard.common.messages.observer.StateMessage; import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage; import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.GameTestUtils;
import eu.jonahbauer.wizard.core.GameTestUtils; import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.MessageQueue; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.states.game.Finished; import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
import eu.jonahbauer.wizard.core.states.trick.StartingTrick; import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@ -20,9 +21,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish; import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry; import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -36,7 +37,7 @@ public class PredictingTest {
@SneakyThrows @SneakyThrows
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private Game performTest(Configuration configuration, int round, MessageQueue queue) { private Game performTest(GameConfiguration configuration, int round, MessageQueue queue) {
Map<UUID, List<Card>> hands = Map.of( Map<UUID, List<Card>> hands = Map.of(
players[0], Collections.nCopies(round + 1, Card.HIDDEN), players[0], Collections.nCopies(round + 1, Card.HIDDEN),
players[1], Collections.nCopies(round + 1, Card.HIDDEN), players[1], Collections.nCopies(round + 1, Card.HIDDEN),
@ -44,7 +45,7 @@ public class PredictingTest {
players[3], Collections.nCopies(round + 1, Card.HIDDEN) players[3], Collections.nCopies(round + 1, Card.HIDDEN)
); );
Game game = spy(new Game(configuration, 10 * 60 * 1000, 10 * 60 * 1000, queue)); Game game = spy(new Game(configuration, queue));
doFinish().when(game).transition(any(StartingTrick.class)); doFinish().when(game).transition(any(StartingTrick.class));
queue.setGame(game); queue.setGame(game);
@ -76,7 +77,7 @@ public class PredictingTest {
.addPrediction(players[2], 3) .addPrediction(players[2], 3)
.addPrediction(players[3], 0); .addPrediction(players[3], 0);
Game game = performTest(Configuration.ANNIVERSARY_2021, 3, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 3, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -118,7 +119,7 @@ public class PredictingTest {
.addPrediction(players[3], 0) .addPrediction(players[3], 0)
.end(); .end();
Game game = performTest(Configuration.ANNIVERSARY_2021, 3, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 3, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -152,7 +153,7 @@ public class PredictingTest {
.addPrediction(players[3], 0) .addPrediction(players[3], 0)
.end(); .end();
Game game = performTest(Configuration.ANNIVERSARY_2021_PM1, 3, queue); Game game = performTest(Configurations.ANNIVERSARY_2021_PM1, 3, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);

@ -1,12 +1,13 @@
package eu.jonahbauer.wizard.core.states.round; package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.common.messages.observer.*; import eu.jonahbauer.wizard.common.messages.observer.*;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.MessageQueue; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.states.game.Finished; import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@ -16,9 +17,9 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static eu.jonahbauer.wizard.core.GameTestUtils.finish; import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry; import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -31,8 +32,8 @@ public class RoundTest {
}; };
@SneakyThrows @SneakyThrows
private Game performTest(long seed, Configuration configuration, int round, MessageQueue queue) { private Game performTest(long seed, GameConfiguration configuration, int round, MessageQueue queue) {
Game game = spy(new Game(seed, configuration, 10 * 60 * 1000, 10 * 60 * 1000, queue)); Game game = spy(new Game(seed, configuration, queue));
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingRound.class)); doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingRound.class));
queue.setGame(game); queue.setGame(game);
@ -105,7 +106,7 @@ public class RoundTest {
.addCard(players[2], Card.BLUE_6); .addCard(players[2], Card.BLUE_6);
int round = 6; int round = 6;
Game game = performTest(0L, Configuration.DEFAULT, round, queue); Game game = performTest(0L, Configurations.DEFAULT, round, queue);
game.await(); game.await();
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -231,7 +232,7 @@ public class RoundTest {
.end(); .end();
int round = 6; int round = 6;
Game game = performTest(227L, Configuration.ANNIVERSARY_2021_PM1, round, queue); Game game = performTest(227L, Configurations.ANNIVERSARY_2021_PM1, round, queue);
game.await(); game.await();
InOrder order = inOrder(game); InOrder order = inOrder(game);

@ -1,13 +1,14 @@
package eu.jonahbauer.wizard.core.states.trick; package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.common.messages.observer.*; import eu.jonahbauer.wizard.common.messages.observer.*;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.GameTestUtils;
import eu.jonahbauer.wizard.core.GameTestUtils; import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.MessageQueue; import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.states.GameData; import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.core.states.round.FinishingRound; import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@ -16,10 +17,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish; import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.GameTestUtils.finish; import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.states.GameData.*; import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry; import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -34,8 +35,8 @@ public class TrickTest {
@SneakyThrows @SneakyThrows
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private Game performTest(Configuration configuration, int round, int trick, Map<UUID, List<Card>> hands, Card.Suit trump, MessageQueue queue) { private Game performTest(GameConfiguration configuration, int round, int trick, Map<UUID, List<Card>> hands, Card.Suit trump, MessageQueue queue) {
Game game = spy(new Game(configuration, 10 * 60 * 1000, 10 * 60 * 1000, queue)); Game game = spy(new Game(configuration, queue));
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingTrick.class)); doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingTrick.class));
doFinish().when(game).transition(any(FinishingRound.class)); doFinish().when(game).transition(any(FinishingRound.class));
queue.setGame(game); queue.setGame(game);
@ -74,7 +75,7 @@ public class TrickTest {
.sync(players) .sync(players)
.addCards(List.of(players), hands, 0); .addCards(List.of(players), hands, 0);
Game game = performTest(Configuration.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue); Game game = performTest(Configurations.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -115,7 +116,7 @@ public class TrickTest {
.addCard(players[3], Card.BLUE_1) .addCard(players[3], Card.BLUE_1)
.end(); .end();
Game game = performTest(Configuration.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue); Game game = performTest(Configurations.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -158,7 +159,7 @@ public class TrickTest {
.addCard(players[3], Card.BLUE_1) .addCard(players[3], Card.BLUE_1)
.addChangePrediction(players[2], 1); .addChangePrediction(players[2], 1);
Game game = performTest(Configuration.ANNIVERSARY_2021, 0, 0, hands, Card.Suit.GREEN, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 0, 0, hands, Card.Suit.GREEN, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -202,7 +203,7 @@ public class TrickTest {
.addJuggle(players[3], Card.BLUE_1) .addJuggle(players[3], Card.BLUE_1)
.end(); .end();
Game game = performTest(Configuration.ANNIVERSARY_2021, 1, 0, hands, Card.Suit.YELLOW, queue); Game game = performTest(Configurations.ANNIVERSARY_2021, 1, 0, hands, Card.Suit.YELLOW, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -247,7 +248,7 @@ public class TrickTest {
.addCard(players[2], Card.GREEN_1) .addCard(players[2], Card.GREEN_1)
.addCard(players[3], Card.BLUE_1); .addCard(players[3], Card.BLUE_1);
Game game = performTest(Configuration.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue); Game game = performTest(Configurations.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);
@ -289,7 +290,7 @@ public class TrickTest {
.addCard(players[2], Card.GREEN_WIZARD) .addCard(players[2], Card.GREEN_WIZARD)
.addCard(players[3], Card.BLUE_1); .addCard(players[3], Card.BLUE_1);
Game game = performTest(Configuration.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue); Game game = performTest(Configurations.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue);
// validate messages // validate messages
InOrder order = inOrder(game); InOrder order = inOrder(game);

@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.model.card; package eu.jonahbauer.wizard.core.model.card;
import eu.jonahbauer.wizard.common.model.Card; import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.util.Pair;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;

@ -4,22 +4,14 @@ import eu.jonahbauer.wizard.common.messages.server.*;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.server.debug.DebugSession; import eu.jonahbauer.wizard.server.debug.DebugSession;
import eu.jonahbauer.wizard.server.machine.Player; import eu.jonahbauer.wizard.server.machine.Player;
import eu.jonahbauer.wizard.server.management.LobbyMBean;
import eu.jonahbauer.wizard.server.management.SessionMBean;
import lombok.extern.log4j.Log4j2;
import org.intellij.lang.annotations.Language; import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import java.lang.management.ManagementFactory;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@Log4j2 public class Lobby {
public class Lobby implements LobbyMBean {
@Language("RegExp") @Language("RegExp")
private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}"; private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
private static final Lobby INSTANCE = new Lobby(); private static final Lobby INSTANCE = new Lobby();
@ -27,17 +19,12 @@ public class Lobby implements LobbyMBean {
return INSTANCE; return INSTANCE;
} }
private final MBeanServer mBeanServer;
private final Map<UUID, Session> sessions = new ConcurrentHashMap<>(); private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
private final List<Player> players = new ArrayList<>(); private final List<Player> players = new ArrayList<>();
// read lock is required whenever players are read or sessions are modified, write lock is required when players are modified // read lock is required whenever players are read or sessions are modified, write lock is required when players are modified
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private Lobby() { private Lobby() {}
mBeanServer = ManagementFactory.getPlatformMBeanServer();
registerLobbyMBean();
}
public Session createSession(@NotNull String name, long timeout, @NotNull Configuration configuration) { public Session createSession(@NotNull String name, long timeout, @NotNull Configuration configuration) {
if (!name.matches(SESSION_NAME_PATTERN)) { if (!name.matches(SESSION_NAME_PATTERN)) {
@ -55,9 +42,6 @@ public class Lobby implements LobbyMBean {
session = new Session(UUID.randomUUID(), name, timeout, configuration); session = new Session(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null); } while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData())); notifyPlayers(new SessionCreatedMessage(session.toData()));
return session; return session;
@ -74,9 +58,6 @@ public class Lobby implements LobbyMBean {
session = new DebugSession(UUID.randomUUID(), name, timeout, configuration); session = new DebugSession(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null); } while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created debug session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData())); notifyPlayers(new SessionCreatedMessage(session.toData()));
return session; return session;
@ -92,11 +73,8 @@ public class Lobby implements LobbyMBean {
public void removeSession(UUID uuid) { public void removeSession(UUID uuid) {
lock.readLock().lock(); lock.readLock().lock();
try { try {
if (sessions.remove(uuid) != null) { sessions.remove(uuid);
log.info("Removed session {}.", uuid); notifyPlayers(new SessionRemovedMessage(uuid));
unregisterSessionMBean(uuid);
notifyPlayers(new SessionRemovedMessage(uuid));
}
} finally { } finally {
lock.readLock().unlock(); lock.readLock().unlock();
} }
@ -139,53 +117,4 @@ public class Lobby implements LobbyMBean {
lock.readLock().unlock(); lock.readLock().unlock();
} }
} }
//<editor-fold desc="JMX" defaultstate="collapsed">
private void registerLobbyMBean() {
try {
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Lobby");
mBeanServer.registerMBean(new StandardMBean(this, LobbyMBean.class), name);
} catch (Exception e) {
log.warn("Could not register LobbyMBean.", e);
}
}
private void registerSessionMBean(@NotNull Session session) {
try {
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Session,name=" + session.getUuid());
mBeanServer.registerMBean(new StandardMBean(session, SessionMBean.class), name);
} catch (Exception e) {
log.warn("Could not register SessionMBean for session {}.", session.getUuid(), e);
}
}
private void unregisterSessionMBean(@NotNull UUID uuid) {
try {
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Session,name=" + uuid);
mBeanServer.unregisterMBean(name);
} catch (Exception e) {
log.warn("Could not unregister SessionMBean for session {}.", uuid, e);
}
}
@Override
public int getPlayerCount() {
lock.readLock().lock();
try {
return players.size();
} finally {
lock.readLock().unlock();
}
}
@Override
public int getTotalPlayerCount() {
return sessions.values().stream().mapToInt(Session::getPlayerCount).sum() + getPlayerCount();
}
@Override
public int getSessionCount() {
return sessions.size();
}
//</editor-fold>
} }

@ -1,5 +1,6 @@
package eu.jonahbauer.wizard.server; package eu.jonahbauer.wizard.server;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData; import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.data.SessionData; import eu.jonahbauer.wizard.common.messages.data.SessionData;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage; import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
@ -8,11 +9,11 @@ import eu.jonahbauer.wizard.common.messages.player.ContinueMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.server.*; import eu.jonahbauer.wizard.common.messages.server.*;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game; import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.Observer; import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.common.util.Pair; import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.util.Pair;
import eu.jonahbauer.wizard.server.machine.Player; import eu.jonahbauer.wizard.server.machine.Player;
import eu.jonahbauer.wizard.server.management.SessionMBean;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -28,7 +29,7 @@ import java.util.concurrent.ThreadLocalRandom;
@Getter @Getter
@Log4j2 @Log4j2
@EqualsAndHashCode(of = "uuid") @EqualsAndHashCode(of = "uuid")
public class Session implements Observer, SessionMBean { public class Session implements Observer {
@Language("RegExp") @Language("RegExp")
private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}"; private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
protected static final int MIN_PLAYERS = 3; protected static final int MIN_PLAYERS = 3;
@ -53,11 +54,6 @@ public class Session implements Observer, SessionMBean {
this.configuration = configuration; this.configuration = configuration;
} }
@Override
public boolean isRunning() {
return game != null;
}
/** /**
* Associates the given player with this session and 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 * session with a {@link PlayerJoinedMessage}, the joining player with a {@link SessionJoinedMessage} and all
@ -68,7 +64,7 @@ public class Session implements Observer, SessionMBean {
* @return the players uuid * @return the players uuid
*/ */
public synchronized UUID join(Player player, String name) { public synchronized UUID join(Player player, String name) {
if (isRunning()) { if (game != null) {
throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started."); throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started.");
} else if (players.size() == MAX_PLAYERS) { } else if (players.size() == MAX_PLAYERS) {
throw new NackException(NackMessage.SESSION_FULL, "Session is full."); throw new NackException(NackMessage.SESSION_FULL, "Session is full.");
@ -85,7 +81,7 @@ public class Session implements Observer, SessionMBean {
sessionPlayer.setPlayer(player); sessionPlayer.setPlayer(player);
notifyJoined(sessionPlayer.toData()); notifyJoined(sessionPlayer.toData());
notifyLobby(); Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
return sessionPlayer.getUuid(); return sessionPlayer.getUuid();
} }
@ -120,13 +116,13 @@ public class Session implements Observer, SessionMBean {
} }
public synchronized void leave(UUID player) { public synchronized void leave(UUID player) {
if (!isRunning()) { if (game == null) {
if (players.remove(player) != null) { if (players.remove(player) != null) {
if (players.size() == 0) { if (players.size() == 0) {
Lobby.getInstance().removeSession(uuid); Lobby.getInstance().removeSession(uuid);
} else { } else {
notifyPlayers(new PlayerLeftMessage(player)); notifyPlayers(new PlayerLeftMessage(player));
notifyLobby(); Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
} }
} }
} else { } else {
@ -144,7 +140,7 @@ public class Session implements Observer, SessionMBean {
var player = players.get(uuid); var player = players.get(uuid);
if (player == null) { if (player == null) {
throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?"); throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?");
} else if (isRunning()) { } else if (game != null) {
throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started."); throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started.");
} }
@ -169,11 +165,15 @@ public class Session implements Observer, SessionMBean {
var player = players.get(uuid); var player = players.get(uuid);
if (player == null) { if (player == null) {
throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?"); throw new NackException(NackMessage.PLAYER_NOT_FOUND, "Who are you?");
} else if (!isRunning()) { } else if (game == null) {
throw new NackException(NackMessage.GAME_NOT_YET_STARTED, "Game hat not yet started."); throw new NackException(NackMessage.GAME_NOT_YET_STARTED, "Game hat not yet started.");
} else { } else {
try { try {
game.onMessage(uuid, message, player::buffer); game.execute(s -> {
// start buffering while game is locked
player.buffer();
return s.onMessage(game, uuid, message);
});
player.send(new AckMessage()); player.send(new AckMessage());
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw new NackException(NackMessage.ILLEGAL_STATE, e.getMessage()); throw new NackException(NackMessage.ILLEGAL_STATE, e.getMessage());
@ -183,11 +183,10 @@ public class Session implements Observer, SessionMBean {
} }
} }
protected synchronized void startGame() { protected void startGame() {
notifyPlayers(new StartingGameMessage()); notifyPlayers(new StartingGameMessage());
messages.add(Pair.of(null, new StartingGameMessage())); messages.add(Pair.of(null, new StartingGameMessage()));
game = new Game(configuration, timeout, 10 * 60 * 1000, this); game = new Game(Configurations.get(configuration).withTimeout(timeout), this);
notifyLobby();
game.start(List.copyOf(players.keySet())); game.start(List.copyOf(players.keySet()));
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
while (true) { while (true) {
@ -201,23 +200,20 @@ public class Session implements Observer, SessionMBean {
break; break;
} }
} }
finishGame(); players.forEach((id, player) -> player.setReady(false));
}); synchronized (this) {
} game = null;
messages.clear();
protected synchronized void finishGame() { for (SessionPlayer player : List.copyOf(players.values())) {
players.forEach((id, player) -> player.setReady(false)); if (!player.isConnected()) {
game = null; leave(player.getUuid());
messages.clear(); }
for (SessionPlayer player : List.copyOf(players.values())) { }
if (!player.isConnected()) {
leave(player.getUuid());
} }
} });
notifyLobby();
} }
protected synchronized void notifyJoined(PlayerData joined) { protected void notifyJoined(PlayerData joined) {
var message = new PlayerJoinedMessage(joined); var message = new PlayerJoinedMessage(joined);
for (SessionPlayer player : players.values()) { for (SessionPlayer player : players.values()) {
if (player.getUuid().equals(joined.getUuid())) { if (player.getUuid().equals(joined.getUuid())) {
@ -233,14 +229,14 @@ public class Session implements Observer, SessionMBean {
} }
} }
protected synchronized void notifyPlayers(ServerMessage message) { protected void notifyPlayers(ServerMessage message) {
for (SessionPlayer player : players.values()) { for (SessionPlayer player : players.values()) {
player.send(message); player.send(message);
} }
} }
public SessionData toData() { public SessionData toData() {
return new SessionData(uuid, name, players.size(), configuration, isRunning()); return new SessionData(uuid, name, players.size(), configuration);
} }
@Override @Override
@ -275,30 +271,6 @@ public class Session implements Observer, SessionMBean {
} }
} }
protected void notifyLobby() {
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
}
public void close() {
for (var sessionPlayer : getPlayers().values()) {
var player = sessionPlayer.getPlayer();
if (player != null) {
player.disconnect("Session was forcibly closed.");
}
}
}
//<editor-fold desc="JMX" defaulstate="collapsed">
public boolean isDebug() {
return false;
}
@Override
public synchronized int getPlayerCount() {
return players.size();
}
//</editor-fold>
@Data @Data
@EqualsAndHashCode(of = "uuid") @EqualsAndHashCode(of = "uuid")
protected static class SessionPlayer { protected static class SessionPlayer {

@ -3,11 +3,14 @@ package eu.jonahbauer.wizard.server.debug;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage; import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.server.NackMessage; import eu.jonahbauer.wizard.common.messages.server.NackMessage;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage; import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.common.messages.server.SessionModifiedMessage;
import eu.jonahbauer.wizard.common.model.Configuration; import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.server.Lobby;
import eu.jonahbauer.wizard.server.NackException; import eu.jonahbauer.wizard.server.NackException;
import eu.jonahbauer.wizard.server.Session; import eu.jonahbauer.wizard.server.Session;
import eu.jonahbauer.wizard.server.machine.Player; import eu.jonahbauer.wizard.server.machine.Player;
import java.io.IOException;
import java.util.UUID; import java.util.UUID;
public class DebugSession extends Session { public class DebugSession extends Session {
@ -49,7 +52,7 @@ public class DebugSession extends Session {
sessionPlayer.setPlayer(player); sessionPlayer.setPlayer(player);
notifyJoined(sessionPlayer.toData()); notifyJoined(sessionPlayer.toData());
notifyLobby(); Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
return sessionPlayer.getUuid(); return sessionPlayer.getUuid();
} }
@ -57,13 +60,19 @@ public class DebugSession extends Session {
@Override @Override
protected void startGame() {} protected void startGame() {}
@Override public void close() {
public void notifyPlayers(ServerMessage message) { for (var sessionPlayer : getPlayers().values()) {
super.notifyPlayers(message); try {
var player = sessionPlayer.getPlayer();
if (player != null) {
player.disconnect();
}
} catch (IOException ignored) {}
}
} }
@Override @Override
public boolean isDebug() { public void notifyPlayers(ServerMessage message) {
return true; super.notifyPlayers(message);
} }
} }

@ -6,7 +6,6 @@ import eu.jonahbauer.wizard.common.messages.server.Response;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage; import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.server.machine.states.CreatedState; import eu.jonahbauer.wizard.server.machine.states.CreatedState;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
@ -45,16 +44,8 @@ public class Player extends Context<ClientState, Player> {
shouldBuffer = Response.class; shouldBuffer = Response.class;
} }
public void disconnect() { public void disconnect() throws IOException {
disconnect(null); session.close(CloseStatus.SERVER_ERROR);
}
public void disconnect(@Nullable String reason) {
try {
session.close(new CloseStatus(CloseStatus.GOING_AWAY.getCode(), reason));
} catch (IOException ignored) {
// ignored
}
} }
@SneakyThrows @SneakyThrows

@ -1,11 +0,0 @@
package eu.jonahbauer.wizard.server.management;
@SuppressWarnings("unused")
public interface LobbyMBean {
int getPlayerCount();
int getTotalPlayerCount();
int getSessionCount();
}

@ -1,19 +0,0 @@
package eu.jonahbauer.wizard.server.management;
import java.util.UUID;
@SuppressWarnings("unused")
public interface SessionMBean {
UUID getUuid();
String getName();
int getPlayerCount();
boolean isRunning();
boolean isDebug();
void close();
}
Loading…
Cancel
Save