Compare commits

..

105 Commits

Author SHA1 Message Date
jonah d3cf34a6c5 removed unused resources 2022-01-31 22:20:53 +01:00
Johannes Hochwart fc61c206c6 Netzwerkkomponente(#9) 2022-01-26 23:03:32 +01:00
Johannes Hochwart e8ae89b635 Anleitungsscreen(#18) 2022-01-26 15:02:03 +01:00
jonah aa5d6febc5 fixed npe in CardUtil.getDefaultTrumpSuit 2022-01-17 18:19:28 +01:00
jonah 46ab8f4ead added sync before determining trump 2022-01-17 15:41:19 +01:00
jonah 6d0e0fed03 fixed server build script 2022-01-17 12:19:52 +01:00
jonah 41e99659be improved error handling 2022-01-17 00:41:27 +01:00
jonah 6c653f1aee fixed npe in Session 2022-01-17 00:38:49 +01:00
jonah 89b04be4b7 fixed bug in ChangePredictionOverlay 2022-01-16 23:47:36 +01:00
jonah 2d02c9f41a improved error handling 2022-01-16 23:07:29 +01:00
jonah c614af3d8d added "end card" 2022-01-15 00:47:17 +01:00
jonah 87577d5779 added trick count label 2022-01-14 23:20:44 +01:00
jonah 9d7ce76e56 bugfixes 2022-01-14 22:41:51 +01:00
jonah 6432412780 added support for changeling and improved support for cloud and juggler 2022-01-14 21:57:30 +01:00
jonah f2693a6ed2 improved instructions screen (#18) 2022-01-14 13:58:06 +01:00
Johannes Hochwart bc1613c448 Anleitungsscreen(#18) 2022-01-14 13:31:10 +01:00
jonah 67773ee2b3 multiple bugfixes 2022-01-14 09:15:10 +01:00
jonah 8137f51617 Merge remote-tracking branch 'origin/main' into main 2022-01-14 09:01:03 +01:00
jonah c7b48c6011 font adjustments 2022-01-14 09:00:39 +01:00
Benedikt Riedl 7b6e55b801 Sound Effects 2022-01-14 02:07:18 +01:00
Johannes Hochwart ed7e9d61aa Anleitungsscreen(#18) 2022-01-14 00:32:13 +01:00
Johannes Teubler 8463903296 #16 started useful error handling 2022-01-13 23:09:20 +01:00
Johannes Teubler 9aa3bbae74 #16 added basic error screen 2022-01-13 22:30:09 +01:00
Johannes Teubler b9e0718dc9 #16 added internationalized messages 2022-01-13 22:25:39 +01:00
jonah 480d4b5353 rejoin support for libgdx client 2022-01-13 22:05:47 +01:00
jonah 4d2ab94290 bugfixes 2022-01-13 22:04:54 +01:00
jonah d954db3189 refactoring 2022-01-13 19:26:26 +01:00
jonah b1455b5f5a refactoring 2022-01-13 19:23:52 +01:00
jonah 8b2a2c400f bugfixes 2022-01-13 16:30:09 +01:00
jonah 6914e6fee7 added name validation 2022-01-13 16:25:47 +01:00
jonah 7bc9aff799 fixed dimming behind overlays
added menu
2022-01-12 21:11:57 +01:00
jonah b0ff831932 migration to asset manager 2022-01-12 16:01:49 +01:00
jonah 2bb14b6842 improved menu accessibility 2022-01-12 14:44:04 +01:00
jonah 1214db6627 bugfixes 2022-01-12 12:51:22 +01:00
jonah c71d5b3336 added debug websocket 2022-01-12 12:15:52 +01:00
jonah a55240f029 improved null safety 2022-01-12 11:35:02 +01:00
jonah 9baf7f8079 migrated to jackson 2022-01-12 10:28:31 +01:00
jonah b2e506d87a bugfixes and improvements
* fixed inconsistent player order
* minor visual adjustments
* refactoring
2022-01-11 14:00:28 +01:00
jonah fa81d56b71 support for juggling and multicolored cards 2022-01-10 22:00:37 +01:00
jonah 55f4a01381 changes to sync 2022-01-10 20:22:07 +01:00
Benedikt Riedl 0e394614c0 Kartenbilder jpg@250x400px 2022-01-10 16:10:59 +01:00
jonah 7b07c5980b internationalization of game screen 2022-01-10 15:12:42 +01:00
jonah 33af2c9791 game logic 2022-01-10 14:30:27 +01:00
jonah 5e3db1d0a5 added sync point after trump determination 2022-01-10 04:06:27 +01:00
jonah b0e6df12c8 improved menus in libGDX client 2022-01-10 03:10:32 +01:00
jonah 0fcf612841 improved libGDX client logging 2022-01-09 22:49:32 +01:00
jonah f16ae15858 added suit cards 2022-01-09 22:24:08 +01:00
jonah dd76511af2 Updated Log4j2 dependency 2022-01-09 22:23:54 +01:00
jonah 5f05501e6a visual improvements 2021-12-14 19:28:02 +01:00
jonah 17ade468a3 added sync at start of round and trick 2021-12-14 17:34:20 +01:00
jonah cae83fe2f9 added TimeoutMessage 2021-12-14 16:26:02 +01:00
jonah 04524a8a0b visual improvements 2021-12-14 14:00:59 +01:00
jonah 54eeef1c0c improved CreateGameScreen 2021-12-14 12:55:18 +01:00
jonah a0dac7f924 fixed game creation screen fde17ed9 #16 2021-12-14 11:16:26 +01:00
jonah f1654c7e1f Update .gitlab-ci.yml file 2021-12-14 10:59:04 +01:00
jonah 3a374e2f05 Updated Dependencies 2021-12-14 09:03:10 +01:00
Johannes Teubler fde17ed9e1 #16 + updated CreateGameScreen
functionality of CreateGameScreen WIP
2021-12-09 21:36:40 +01:00
jonah a1767897d2 fixed b17cb491 (#17) 2021-12-07 17:25:59 +01:00
jonah 0c2190baea reconnect in server and cli 2021-12-05 11:21:46 +01:00
Johannes Hochwart b17cb49114 Spielscreen (#17) 2021-12-04 15:14:00 +01:00
jonah d7e2e3d7f0 #17 2021-12-03 10:53:52 +01:00
jonah 1b0cce5a48 Cleanup 2021-12-02 22:44:27 +01:00
jonah f022e0e0c7 Cleanup 2021-12-02 22:24:45 +01:00
Benedikt Riedl 4307ff48dc Kartenbilder jpg@250x400px 2021-12-02 14:45:03 +01:00
Benedikt Riedl 7a7a0b192b Einzelgrafikelemente #7 2021-12-02 03:02:30 +01:00
jonah cf7f22163c shrank client size 2021-12-01 17:58:53 +01:00
jonah d0763ccf53 improved GameScreen 2021-12-01 14:28:03 +01:00
jonah b35a19274a bugfixes in server and cli-client 2021-11-30 11:23:41 +01:00
Alexander Hirmer 153d0223db #14 2021-11-29 15:56:32 +01:00
jonah 92b95501c9 CLI Client #15 2021-11-25 23:17:29 +01:00
jonah fc09a982e4 CLI Client #15 2021-11-25 19:36:52 +01:00
jonah b7bd3c7ade CLI Client #15 2021-11-25 19:33:31 +01:00
Johannes Hochwart 29195be75d Erste Grundlage für den Spielscreen (#17) 2021-11-25 19:28:14 +01:00
Johannes Hochwart cbe674bb0b Erste Grundlage für den Spielscreen (#17) 2021-11-25 19:19:55 +01:00
Alexander Hirmer 1fbdd40dda Ticket #14 2021-11-23 16:00:14 +01:00
jonah 79f4ea3fe1 improved libGDX client performance
added automatic texture packing
2021-11-19 17:55:46 +01:00
jonah bc3ccb6e81 extracted dependencies info into Dependencies.kt 2021-11-19 10:42:31 +01:00
jonah f530f0af9f migration to gradle kotlin dsl 2021-11-19 09:59:40 +01:00
jonah 7a0f30f8b7 added distribution task to client buildscript 2021-11-17 12:31:35 +01:00
jonah 8d7ab3e6e9 added parse method to messages 2021-11-15 18:29:25 +01:00
jonah 0ced7694e1 Refactored LibGDX Client 2021-11-12 22:34:14 +01:00
Johannes Teubler f29e623a00 Initial libGDX Code Commit #11 2021-11-12 18:34:00 +01:00
jonah a1e05ddafc Sample LibGDX project setup 2021-11-12 18:16:28 +01:00
Benedikt Riedl 09ceee91d6 Einzelgrafikelemente #7 2021-11-12 10:25:55 +01:00
jonah 9341637198 Fixed Gradle Tests 2021-11-12 09:23:49 +01:00
jonah 692fb1cb2c Migration from Maven to Gradle 7.3 2021-11-12 09:06:59 +01:00
Johannes Hochwart 5dd7cc6eab Weitere Client- und ServerMessages für Verbindungsverlust und VoteCick 2021-11-11 13:06:08 +01:00
jonah 1bf5e127fe Reworked state machine 2021-11-11 09:48:59 +01:00
Johannes Hochwart 90625e536b Merge remote-tracking branch 'origin/main' 2021-11-10 20:27:40 +01:00
Johannes Hochwart 4b13b4ef16 Bugfixes 2021-11-10 20:27:19 +01:00
Benedikt Riedl 075ac3746e Upload New File 2021-11-09 12:38:29 +00:00
Benedikt Riedl e0ce3cc80d Upload New File 2021-11-09 12:38:09 +00:00
Benedikt Riedl f2462ff43e Upload New File 2021-11-09 12:37:33 +00:00
Benedikt Riedl dfb21aaee5 Upload New File 2021-11-09 12:36:56 +00:00
Benedikt Riedl 8b3f93aca1 Menügrafiken 2021-11-09 12:36:06 +00:00
Benedikt Riedl e8c911c38c Konzeptgrafiken 2021-11-09 12:35:45 +00:00
Johannes Hochwart 262ae4a2cc Client- und Server-Nachrichten verbessert(#9) 2021-11-05 12:43:58 +01:00
jonah f4255d39ab Fehler in Spiellogik von Jongleur und Wolke behoben (#12) 2021-11-05 01:31:20 +01:00
Johannes Hochwart 6da10c6b43 Client- und Server-Nachrichten implementiert(#9) 2021-11-04 22:54:40 +01:00
jonah 66f6d10330 - Jubiläumsedition implementiert (#12)
- Tests verbessert
2021-11-04 20:18:26 +01:00
jonah 1775604e1d - refactored GameData
- removed reference to Context in State
2021-10-29 22:58:11 +02:00
Jonah Bauer d04e894937 Update .gitlab-ci.yml file 2021-10-28 21:05:47 +00:00
jonah d8453fa527 Issue #5: Grundspiel implementieren 2021-10-28 23:04:49 +02:00
jonah a6f82361f7 project setup 2021-10-18 16:30:07 +02:00
Jonah Bauer ca5401cbe2 Initial commit 2021-10-11 13:50:11 +00:00
93 changed files with 493 additions and 684 deletions
@@ -2,8 +2,8 @@ package eu.jonahbauer.wizard.client.cli.commands;
import eu.jonahbauer.wizard.client.cli.Client;
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.util.Pair;
import java.util.List;
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.commands.GameCommand;
import eu.jonahbauer.wizard.client.cli.util.Pair;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
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.ServerMessage;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.util.Pair;
import lombok.Getter;
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;
import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.ClientState;
import picocli.CommandLine;
@@ -9,7 +9,6 @@ import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import eu.jonahbauer.wizard.client.libgdx.screens.MainMenuScreen;
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 lombok.Getter;
@@ -20,8 +19,6 @@ public class WizardGame extends Game {
public SpriteBatch batch;
public WizardAssetManager assets;
public SoundManager sounds;
public final SavedData storage = new SavedData();
private boolean fullscreenToggle;
@@ -38,8 +35,6 @@ public class WizardGame extends Game {
assets.loadShared();
assets.finishLoading();
sounds = new SoundManager(assets);
// background music
Music backgroundMusic = assets.get(WizardAssetManager.MUSIC_BACKGROUND, Music.class);
backgroundMusic.setLooping(true);
@@ -82,7 +77,6 @@ public class WizardGame extends Game {
@Override
public void dispose () {
batch.dispose();
sounds.dispose();
assets.dispose();
client.shutdownNow();
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) {
if (actor == buttonBack) {
game.getClient().execute(Menu.class, Menu::showMenuScreen);
sfxClick();
} else if (actor == buttonConnect) {
try {
var uriString = ConnectScreen.this.uriField.getText();
@@ -38,6 +39,8 @@ public class ConnectScreen extends MenuScreen {
} catch (URISyntaxException e) {
uriField.setStyle(getTextFieldErrorStyle());
}
sfxClick();
}
}
};
@@ -31,8 +31,10 @@ public class CreateGameScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::showListScreen);
sfxClick();
} else if (actor == buttonContinue) {
create();
sfxClick();
}
}
};
@@ -18,6 +18,7 @@ public class ErrorScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) {
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.state.Game;
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.model.Card;
import lombok.Getter;
@@ -418,13 +421,6 @@ public class GameScreen extends WizardScreen {
execute(new StartRoundOverlay(this, round));
}
public void deal() {
execute(parallel(
run(() -> game.sounds.sfxShuffle()),
delay(SoundManager.CARD_SHUFFLE_DURATION)
));
}
public void startTrick() {
clearActivePlayer();
execute(() -> cardStack.clearChildren());
@@ -535,8 +531,6 @@ public class GameScreen extends WizardScreen {
cardStack.add(seat, actor);
sequence.addAction(delay(actor));
sequence.addAction(delay(AnimationTimings.STACK_HOLD));
game.sounds.sfxPlayCard();
}));
execute(sequence);
}
@@ -31,12 +31,15 @@ public class InstructionScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) {
game.getClient().execute(Menu.class, Menu::showMenuScreen);
sfxClick();
} else if (actor == nextPageButton) {
currentPage = MathUtils.clamp(currentPage + 1, 0, MAX_PAGE);
showPage(currentPage);
sfxClick();
} else if (actor == previousPageButton) {
currentPage = MathUtils.clamp(currentPage - 1, 0, MAX_PAGE);
showPage(currentPage);
sfxClick();
}
}
};
@@ -1,5 +1,6 @@
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.VerticalGroup;
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.ui.*;
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.actors.IconList;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener;
import eu.jonahbauer.wizard.client.libgdx.state.Lobby;
@@ -20,6 +17,7 @@ public class LobbyScreen extends MenuScreen {
private TextButton buttonBack;
private TextButton buttonJoin;
private TextButton buttonRejoin;
private TextButton buttonCreate;
private TextField playerName;
@@ -28,7 +26,6 @@ public class LobbyScreen extends MenuScreen {
private Label labelSessionConfiguration;
private UUID selectedSession;
private boolean rejoin = false;
private List<SessionData> sessions;
private ScrollPane sessionListContainer;
@@ -37,14 +34,16 @@ public class LobbyScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::disconnect);
sfxClick();
} else if (actor == buttonJoin) {
if (rejoin) {
game.getClient().execute(Lobby.class, Lobby::showRejoinScreen);
} else {
join();
}
join();
sfxClick();
} else if (actor == buttonCreate) {
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);
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.addListener(listener);
getButtonGroup().addActor(buttonJoin);
getButtonGroup().setWidth(0.55f * WizardGame.WIDTH);
sessions = new IconList<>(skin) {
// TODO better icons
private final Drawable running = skin.getDrawable(UiskinAtlas.NOT_READY);
private final Drawable notRunning = skin.getDrawable(UiskinAtlas.READY);
sessions = new List<>(skin) {
@Override
public String toString(SessionData session) {
return session.getName();
}
@Override
public Drawable getIcon(SessionData item) {
return item.isRunning() ? running : notRunning;
}
};
sessions.addListener(new ChangeListener() {
@Override
@@ -108,11 +102,12 @@ public class LobbyScreen extends MenuScreen {
stage.addActor(content);
stage.addCaptureListener(new KeyboardFocusManager(
sessions, playerName, buttonBack, buttonCreate, buttonJoin
sessions, playerName, buttonBack, buttonCreate, buttonRejoin, buttonJoin
));
buttonBack.setName("button_back");
buttonJoin.setName("button_join");
buttonJoin.setName("button_rejoin");
buttonCreate.setName("button_create");
sessions.setName("session_list");
playerName.setName("player_name");
@@ -199,21 +194,14 @@ public class LobbyScreen extends MenuScreen {
labelSessionPlayerCount.setText(Integer.toString(data.getPlayerCount()));
labelSessionConfiguration.setText(data.getConfiguration().toString());
selectedSession = data.getUuid();
updateRejoin(data.isRunning());
} else {
labelSessionName.setText("");
labelSessionPlayerCount.setText("");
labelSessionConfiguration.setText("");
selectedSession = null;
updateRejoin(false);
}
}
private void updateRejoin(boolean rejoin) {
this.rejoin = rejoin;
buttonJoin.setText(messages.get("menu.lobby." + (rejoin ? "rejoin" : "join")));
}
private void join() {
boolean error = false;
@@ -23,10 +23,13 @@ public class MainMenuScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonPlay) {
game.getClient().execute(Menu.class, Menu::showConnectScreen);
sfxClick();
} else if (actor == buttonQuit) {
sfxClick();
Gdx.app.exit();
} else if (actor == buttonInstruction) {
game.getClient().execute(Menu.class, Menu::showInstructionScreen);
sfxClick();
}
}
};
@@ -26,8 +26,10 @@ public class RejoinScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonBack) {
game.getClient().execute(Lobby.class, Lobby::showListScreen);
sfxClick();
} else if (actor == buttonContinue) {
rejoin();
sfxClick();
}
}
};
@@ -1,12 +1,15 @@
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.ui.*;
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.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.IconList;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.state.Session;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
@@ -14,7 +17,7 @@ import eu.jonahbauer.wizard.common.model.Configuration;
import java.util.UUID;
public class SessionScreen extends MenuScreen {
public class WaitingScreen extends MenuScreen {
private TextButton buttonLeave;
private TextButton buttonReady;
@@ -31,13 +34,15 @@ public class SessionScreen extends MenuScreen {
public void changed(ChangeEvent event, Actor actor) {
if (actor == buttonLeave) {
game.getClient().execute(Session.class, Session::leave);
sfxClick();
} else if (actor == buttonReady) {
game.getClient().execute(Session.class, Session::toggleReady);
sfxClick();
}
}
};
public SessionScreen(WizardGame game) {
public WaitingScreen(WizardGame game) {
super(game);
}
@@ -45,19 +50,19 @@ public class SessionScreen extends MenuScreen {
public void show() {
super.show();
buttonLeave = new TextButton(messages.get("menu.session.leave"), skin);
buttonLeave = new TextButton(messages.get("menu.waiting.leave"), skin);
buttonLeave.addListener(listener);
getButtonGroup().addActor(buttonLeave);
buttonReady = new TextButton(messages.get("menu.session.ready"), skin);
buttonReady = new TextButton(messages.get("menu.waiting.ready"), skin);
buttonReady.addListener(listener);
getButtonGroup().addActor(buttonReady);
getButtonGroup().setWidth(0.55f * WizardGame.WIDTH);
players = new IconList<>(skin) {
private final Drawable ready = skin.getDrawable(UiskinAtlas.READY);
private final Drawable notReady = skin.getDrawable(UiskinAtlas.NOT_READY);
players = new List<>(skin) {
private final TextureRegion ready = skin.getRegion(UiskinAtlas.READY);
private final TextureRegion notReady = skin.getRegion(UiskinAtlas.NOT_READY);
@Override
public String toString(PlayerData player) {
@@ -65,8 +70,16 @@ public class SessionScreen extends MenuScreen {
}
@Override
public Drawable getIcon(PlayerData item) {
return item.isReady() ? ready : notReady;
@SuppressWarnings("SuspiciousNameCombination")
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.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(messages.get("menu.session.session_uuid.label")).row();
infoTable.add(messages.get("menu.waiting.session_uuid.label")).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(messages.get("menu.session.player_name.label")).row();
infoTable.add(messages.get("menu.waiting.player_name.label")).row();
infoTable.add(labelPlayerName).row();
return infoTable;
@@ -125,7 +138,7 @@ public class SessionScreen extends MenuScreen {
}
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) {
@@ -2,6 +2,7 @@ package eu.jonahbauer.wizard.client.libgdx.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
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.WizardGame;
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.util.WizardAssetManager;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
@@ -31,6 +31,7 @@ public abstract class WizardScreen implements Screen {
protected Viewport viewport;
private Image background;
private Sound sfxClick;
protected float offsetX;
protected float offsetY;
@@ -55,10 +56,11 @@ public abstract class WizardScreen implements Screen {
stage = new Stage(viewport);
stage.addListener(new ButtonKeyListener());
stage.addListener(new AutoFocusListener());
stage.addListener(new ButtonClickListener(game.sounds));
stage.setDebugAll(WizardGame.DEBUG);
Gdx.input.setInputProcessor(stage);
sfxClick = assets.get(WizardAssetManager.SFX_CLICK);
}
@Override
@@ -108,4 +110,8 @@ public abstract class WizardScreen implements Screen {
public void dispose() {
stage.dispose();
}
protected void sfxClick() {
sfxClick.play(0.6f);
}
}
@@ -68,7 +68,7 @@ public final class AwaitingJoinSession extends Awaiting {
));
} else {
return Optional.of(new Session(
session, sessionName, configuration,
new SessionData(session, sessionName, -1, configuration),
players,
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.common.messages.client.InteractionMessage;
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.player.*;
import eu.jonahbauer.wizard.common.messages.server.AckMessage;
@@ -117,7 +118,6 @@ public final class Game extends BaseState {
case "starting_round" -> {
return onStartRound(client);
}
case "dealing" -> onDealing();
case "starting_trick" -> onStartTrick();
case "juggling" -> onJuggle();
case "finishing_round" -> onFinishingRound();
@@ -190,10 +190,6 @@ public final class Game extends BaseState {
return Optional.empty();
}
private void onDealing() {
if (gameScreen != null) gameScreen.deal();
}
private void onStartTrick() {
log.info("Trick {} is starting...", trick + 1);
trick ++;
@@ -437,7 +433,7 @@ public final class Game extends BaseState {
private Optional<ClientState> returnToSession() {
return Optional.of(new Session(
session, sessionName, configuration,
new SessionData(session, sessionName, -1, configuration),
players.entrySet().stream()
.map(entry -> new PlayerData(entry.getKey(), entry.getValue(), false))
.toList(),
@@ -1,10 +1,11 @@
package eu.jonahbauer.wizard.client.libgdx.state;
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.ReadyMessage;
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.model.Configuration;
import lombok.Getter;
@@ -15,7 +16,7 @@ import java.util.*;
@Log4j2
@Getter
public final class Session extends BaseState {
private SessionScreen sessionScreen;
private WaitingScreen sessionScreen;
private final UUID self;
@@ -27,14 +28,14 @@ public final class Session extends BaseState {
private boolean sending;
public Session(UUID session, String name, Configuration configuration, Collection<PlayerData> players, UUID self) {
this(session, name, configuration, players, self, false);
public Session(SessionData session, Collection<PlayerData> players, UUID self) {
this(session, players, self, false);
}
public Session(UUID session, String name, Configuration configuration, Collection<PlayerData> players, UUID self, boolean dontSwitchScreen) {
this.session = session;
this.sessionName = name;
this.configuration = configuration;
public Session(SessionData session, Collection<PlayerData> players, UUID self, boolean dontSwitchScreen) {
this.session = session.getUuid();
this.sessionName = session.getName();
this.configuration = session.getConfiguration();
players.forEach(p -> this.players.put(p.getUuid(), p));
this.dontSwitchScreen = dontSwitchScreen;
@@ -121,7 +122,7 @@ public final class Session extends BaseState {
}
public Optional<ClientState> showInfoScreen(Client client) {
sessionScreen = new SessionScreen(client.getGame());
sessionScreen = new WaitingScreen(client.getGame());
client.getGame().setScreen(sessionScreen);
sessionScreen.setPlayers(players.values().toArray(new PlayerData[0]));
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.SkinLoader;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
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_GAME = GameAtlas.$PATH;
public static final String SFX_CLICK = "sound/button_click.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 SFX_CLICK = "button_click_s.mp3";
public static final String MUSIC_BACKGROUND = "background.mp3";
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(MUSIC_BACKGROUND, Music.class);
manager.load(SFX_CLICK, Sound.class);
}
public void loadGame() {
@@ -150,14 +150,14 @@ menu.loading.joining_session=Joining session...
menu.loading.joining_lobby=Joining lobby...
menu.loading.back=Return To Main Menu
menu.session.ready=Ready
menu.session.not_ready=Not Ready
menu.session.leave=Leave
menu.waiting.ready=Ready
menu.waiting.not_ready=Not Ready
menu.waiting.leave=Leave
menu.session.player_name.label=Own Name
menu.session.session_name.label=Session Name
menu.session.session_uuid.label=Session UUID
menu.session.session_configuration.label=Configuration
menu.waiting.player_name.label=Own Name
menu.waiting.session_name.label=Session Name
menu.waiting.session_uuid.label=Session UUID
menu.waiting.session_configuration.label=Configuration
menu.error.malformed_message=Error: Malformed 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.back=Zurück zum Hauptmenü
menu.session.ready=Bereit
menu.session.not_ready=Nicht Bereit
menu.session.leave=Verlassen
menu.waiting.ready=Bereit
menu.waiting.not_ready=Nicht Bereit
menu.waiting.leave=Verlassen
menu.session.player_name.label=Eigener Name
menu.session.session_name.label=Sitzungsname
menu.session.session_uuid.label=Sitzungs-ID
menu.session.session_configuration.label=Spielvariante
menu.waiting.player_name.label=Eigener Name
menu.waiting.session_name.label=Sitzungsname
menu.waiting.session_uuid.label=Sitzungs-ID
menu.waiting.session_configuration.label=Spielvariante
menu.error.malformed_message=Fehler: Missgebildete 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 eu.jonahbauer.wizard.common.messages.ParseException;
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.SneakyThrows;
@@ -26,8 +26,4 @@ public class SessionData {
* Configuration of the session
*/
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 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.SneakyThrows;
@@ -2,7 +2,7 @@ package eu.jonahbauer.wizard.common.messages.player;
import com.fasterxml.jackson.databind.ObjectMapper;
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.SneakyThrows;
@@ -3,7 +3,7 @@ package eu.jonahbauer.wizard.common.messages.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.jonahbauer.wizard.common.messages.ParseException;
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.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.JsonTypeInfo;
@@ -10,10 +10,10 @@ import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import eu.jonahbauer.wizard.common.messages.ParseException;
import eu.jonahbauer.wizard.common.util.StringUtil;
import lombok.experimental.UtilityClass;
import java.lang.reflect.Modifier;
import java.util.Locale;
@UtilityClass
public class SerializationUtil {
@@ -61,7 +61,7 @@ public class SerializationUtil {
if (Modifier.isFinal(modifiers) || subclass.isSealed() && !Modifier.isAbstract(modifiers)) {
var name = subclass.getSimpleName();
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));
}
@@ -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.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.TransientState;
import eu.jonahbauer.wizard.core.states.game.Created;
import eu.jonahbauer.wizard.core.states.game.Error;
import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.machine.states.TransientState;
import eu.jonahbauer.wizard.core.machine.states.game.Created;
import eu.jonahbauer.wizard.core.machine.states.game.Error;
import eu.jonahbauer.wizard.core.messages.Observer;
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 org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.List;
import java.util.Random;
@@ -34,25 +30,20 @@ public final class Game extends TimeoutContext<GameState, Game> {
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());
this.random = random;
this.config = Configurations.get(config).withTimeout(timeout).withSyncTimeout(syncTimeout);
this.random = new Random();
this.config = config;
this.observer = observer;
}
public Game(Configuration config, long timeout, long syncTimeout, Observer observer) {
this(new Random(), config, timeout, syncTimeout, observer);
public Game(long seed, GameConfiguration config, Observer observer) {
super(new Created());
this.random = new Random(seed);
this.config = config;
this.observer = observer;
}
@TestOnly
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) {
transition(new Created(), state);
}
@@ -65,17 +56,10 @@ public final class Game extends TimeoutContext<GameState, Game> {
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
protected void onTransition(GameState from, GameState to) {
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.messages.player.ContinueMessage;
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 org.jetbrains.annotations.Unmodifiable;
@@ -12,8 +13,8 @@ import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import static eu.jonahbauer.wizard.core.states.GameData.CURRENT_PLAYER;
import static eu.jonahbauer.wizard.core.states.GameData.PLAYERS;
import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
@Unmodifiable
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.util.Pair;
import eu.jonahbauer.wizard.core.util.Pair;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
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 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.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.ContinueMessage;
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.Optional;
@@ -13,7 +14,7 @@ import java.util.UUID;
import java.util.function.Function;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.SYNC;
import static eu.jonahbauer.wizard.core.states.GameData.PLAYERS;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
public final class SyncState extends GameState implements TransientState {
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.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.List;
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.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import lombok.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.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
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.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
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 {
@@ -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.TimeoutMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
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.InvalidDataException;
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.InvalidDataException;
import java.util.HashMap;
import java.util.Map;
@@ -16,7 +16,7 @@ import java.util.Optional;
import java.util.UUID;
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 {
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.model.Card;
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.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.model.deck.Deck;
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 {
@@ -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.core.states.GameData;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
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 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 {
@@ -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.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.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.TransientState;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.TransientState;
import org.jetbrains.annotations.NotNull;
import java.util.*;
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.states.GameData.TRUMP_CARD;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.TRUMP_CARD;
public final class DeterminingTrumpUserInput extends RoundState implements TransientState {
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.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.game.Finishing;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.game.Finishing;
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
import java.util.HashMap;
@@ -11,7 +11,7 @@ import java.util.Map;
import java.util.Optional;
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 {
@@ -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.core.states.GameData;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.trick.StartingTrick;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
@@ -16,7 +16,7 @@ import java.util.Map;
import java.util.Optional;
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;
@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.states.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
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 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.states.GameState;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
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.core.states.GameData;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.InvalidDataException;
import eu.jonahbauer.wizard.core.states.round.ChangingPrediction;
import eu.jonahbauer.wizard.core.states.round.FinishingRound;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import eu.jonahbauer.wizard.core.machine.states.round.ChangingPrediction;
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.common.messages.observer.TrickMessage;
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 static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
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.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.PlayerMessage;
import eu.jonahbauer.wizard.common.model.Card;
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.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.GameState;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
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 {
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.model.Card;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.states.InvalidDataException;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import eu.jonahbauer.wizard.common.messages.observer.CardMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
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 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;
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.states.GameState;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
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.states.round.RoundState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
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 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;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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 java.util.Comparator;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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.UUID;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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.UUID;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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 java.util.List;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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 java.util.List;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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.RequiredArgsConstructor;
import org.jetbrains.annotations.Contract;
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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 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;
@@ -1,11 +1,11 @@
package eu.jonahbauer.wizard.common.util;
package eu.jonahbauer.wizard.core.util;
import lombok.experimental.UtilityClass;
import java.util.Locale;
@UtilityClass
public class StringUtil {
public class Util {
public static String toSnakeCase(String str) {
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.Game;
import eu.jonahbauer.wizard.core.model.Configurations;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
@@ -14,7 +13,7 @@ public class GameTest {
public void runDefault(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game(
repetitionInfo.getCurrentRepetition(),
Configuration.DEFAULT, 0, 0,
Configurations.DEFAULT.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg)
);
var players = List.of(
@@ -32,7 +31,7 @@ public class GameTest {
public void runAnniversary2016(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game(
repetitionInfo.getCurrentRepetition(),
Configuration.ANNIVERSARY_2016, 0, 0,
Configurations.ANNIVERSARY_2016.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg)
);
var players = List.of(
@@ -50,7 +49,7 @@ public class GameTest {
public void runAnniversary2021(RepetitionInfo repetitionInfo) throws InterruptedException, ExecutionException {
Game game = new Game(
repetitionInfo.getCurrentRepetition(),
Configuration.ANNIVERSARY_2021, 0, 0,
Configurations.ANNIVERSARY_2021.withTimeout(0).withSyncTimeout(0),
(player, msg) -> System.out.println(msg)
);
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 org.mockito.Mockito;
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.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.*;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.GameState;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.messages.Observer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
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 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.StateMessage;
import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.MessageQueue;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.SyncState;
import eu.jonahbauer.wizard.core.states.game.Finished;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.SyncState;
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
@@ -19,9 +20,9 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry;
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@@ -35,8 +36,8 @@ public class DeterminingTrumpTest {
@SneakyThrows
@SuppressWarnings("SameParameterValue")
private Game performTest(Configuration 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));
private Game performTest(GameConfiguration configuration, int round, Map<UUID, List<Card>> hands, Card trumpCard, MessageQueue queue) {
Game game = spy(new Game(configuration, queue));
doFinish().when(game).transition(argThat(argument -> argument instanceof SyncState && argument.getData().has(TRUMP_SUIT)));
queue.setGame(game);
@@ -69,7 +70,7 @@ public class DeterminingTrumpTest {
// play cards in given order
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
InOrder order = inOrder(game);
@@ -94,7 +95,7 @@ public class DeterminingTrumpTest {
MessageQueue queue = new MessageQueue()
.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
InOrder order = inOrder(game);
@@ -120,7 +121,7 @@ public class DeterminingTrumpTest {
.sync(players)
.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
InOrder order = inOrder(game);
@@ -148,7 +149,7 @@ public class DeterminingTrumpTest {
.sync(players)
.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
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.StateMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.GameTestUtils;
import eu.jonahbauer.wizard.core.MessageQueue;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.game.Finished;
import eu.jonahbauer.wizard.core.states.trick.StartingTrick;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameTestUtils;
import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
@@ -20,9 +21,9 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry;
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@@ -36,7 +37,7 @@ public class PredictingTest {
@SneakyThrows
@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(
players[0], 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)
);
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));
queue.setGame(game);
@@ -76,7 +77,7 @@ public class PredictingTest {
.addPrediction(players[2], 3)
.addPrediction(players[3], 0);
Game game = performTest(Configuration.ANNIVERSARY_2021, 3, queue);
Game game = performTest(Configurations.ANNIVERSARY_2021, 3, queue);
// validate messages
InOrder order = inOrder(game);
@@ -118,7 +119,7 @@ public class PredictingTest {
.addPrediction(players[3], 0)
.end();
Game game = performTest(Configuration.ANNIVERSARY_2021, 3, queue);
Game game = performTest(Configurations.ANNIVERSARY_2021, 3, queue);
// validate messages
InOrder order = inOrder(game);
@@ -152,7 +153,7 @@ public class PredictingTest {
.addPrediction(players[3], 0)
.end();
Game game = performTest(Configuration.ANNIVERSARY_2021_PM1, 3, queue);
Game game = performTest(Configurations.ANNIVERSARY_2021_PM1, 3, queue);
// validate messages
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.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.MessageQueue;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.game.Finished;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
@@ -16,9 +17,9 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import static eu.jonahbauer.wizard.core.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry;
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@@ -31,8 +32,8 @@ public class RoundTest {
};
@SneakyThrows
private Game performTest(long seed, Configuration configuration, int round, MessageQueue queue) {
Game game = spy(new Game(seed, configuration, 10 * 60 * 1000, 10 * 60 * 1000, queue));
private Game performTest(long seed, GameConfiguration configuration, int round, MessageQueue queue) {
Game game = spy(new Game(seed, configuration, queue));
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingRound.class));
queue.setGame(game);
@@ -105,7 +106,7 @@ public class RoundTest {
.addCard(players[2], Card.BLUE_6);
int round = 6;
Game game = performTest(0L, Configuration.DEFAULT, round, queue);
Game game = performTest(0L, Configurations.DEFAULT, round, queue);
game.await();
InOrder order = inOrder(game);
@@ -231,7 +232,7 @@ public class RoundTest {
.end();
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();
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.model.Card;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.GameTestUtils;
import eu.jonahbauer.wizard.core.MessageQueue;
import eu.jonahbauer.wizard.core.states.GameData;
import eu.jonahbauer.wizard.core.states.round.FinishingRound;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.GameTestUtils;
import eu.jonahbauer.wizard.core.machine.MessageQueue;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.model.Configurations;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
@@ -16,10 +17,10 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import static eu.jonahbauer.wizard.core.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.states.GameData.*;
import static eu.jonahbauer.wizard.core.states.GameData.TypedValue.entry;
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@@ -34,8 +35,8 @@ public class TrickTest {
@SneakyThrows
@SuppressWarnings("SameParameterValue")
private Game performTest(Configuration 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));
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, queue));
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingTrick.class));
doFinish().when(game).transition(any(FinishingRound.class));
queue.setGame(game);
@@ -74,7 +75,7 @@ public class TrickTest {
.sync(players)
.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
InOrder order = inOrder(game);
@@ -115,7 +116,7 @@ public class TrickTest {
.addCard(players[3], Card.BLUE_1)
.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
InOrder order = inOrder(game);
@@ -158,7 +159,7 @@ public class TrickTest {
.addCard(players[3], Card.BLUE_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
InOrder order = inOrder(game);
@@ -202,7 +203,7 @@ public class TrickTest {
.addJuggle(players[3], Card.BLUE_1)
.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
InOrder order = inOrder(game);
@@ -247,7 +248,7 @@ public class TrickTest {
.addCard(players[2], Card.GREEN_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
InOrder order = inOrder(game);
@@ -289,7 +290,7 @@ public class TrickTest {
.addCard(players[2], Card.GREEN_WIZARD)
.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
InOrder order = inOrder(game);
@@ -1,7 +1,7 @@
package eu.jonahbauer.wizard.core.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 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.server.debug.DebugSession;
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.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.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Log4j2
public class Lobby implements LobbyMBean {
public class Lobby {
@Language("RegExp")
private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
private static final Lobby INSTANCE = new Lobby();
@@ -27,17 +19,12 @@ public class Lobby implements LobbyMBean {
return INSTANCE;
}
private final MBeanServer mBeanServer;
private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
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
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private Lobby() {
mBeanServer = ManagementFactory.getPlatformMBeanServer();
registerLobbyMBean();
}
private Lobby() {}
public Session createSession(@NotNull String name, long timeout, @NotNull Configuration configuration) {
if (!name.matches(SESSION_NAME_PATTERN)) {
@@ -55,9 +42,6 @@ public class Lobby implements LobbyMBean {
session = new Session(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData()));
return session;
@@ -74,9 +58,6 @@ public class Lobby implements LobbyMBean {
session = new DebugSession(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created debug session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData()));
return session;
@@ -92,11 +73,8 @@ public class Lobby implements LobbyMBean {
public void removeSession(UUID uuid) {
lock.readLock().lock();
try {
if (sessions.remove(uuid) != null) {
log.info("Removed session {}.", uuid);
unregisterSessionMBean(uuid);
notifyPlayers(new SessionRemovedMessage(uuid));
}
sessions.remove(uuid);
notifyPlayers(new SessionRemovedMessage(uuid));
} finally {
lock.readLock().unlock();
}
@@ -139,53 +117,4 @@ public class Lobby implements LobbyMBean {
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;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
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.server.*;
import eu.jonahbauer.wizard.common.model.Configuration;
import eu.jonahbauer.wizard.core.Game;
import eu.jonahbauer.wizard.core.Observer;
import eu.jonahbauer.wizard.common.util.Pair;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.core.model.Configurations;
import eu.jonahbauer.wizard.core.util.Pair;
import eu.jonahbauer.wizard.server.machine.Player;
import eu.jonahbauer.wizard.server.management.SessionMBean;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -28,7 +29,7 @@ import java.util.concurrent.ThreadLocalRandom;
@Getter
@Log4j2
@EqualsAndHashCode(of = "uuid")
public class Session implements Observer, SessionMBean {
public class Session implements Observer {
@Language("RegExp")
private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
protected static final int MIN_PLAYERS = 3;
@@ -53,11 +54,6 @@ public class Session implements Observer, SessionMBean {
this.configuration = configuration;
}
@Override
public boolean isRunning() {
return game != null;
}
/**
* 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
@@ -68,7 +64,7 @@ public class Session implements Observer, SessionMBean {
* @return the players uuid
*/
public synchronized UUID join(Player player, String name) {
if (isRunning()) {
if (game != null) {
throw new NackException(NackMessage.GAME_ALREADY_STARTED, "Game has already started.");
} else if (players.size() == MAX_PLAYERS) {
throw new NackException(NackMessage.SESSION_FULL, "Session is full.");
@@ -85,7 +81,7 @@ public class Session implements Observer, SessionMBean {
sessionPlayer.setPlayer(player);
notifyJoined(sessionPlayer.toData());
notifyLobby();
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
return sessionPlayer.getUuid();
}
@@ -120,13 +116,13 @@ public class Session implements Observer, SessionMBean {
}
public synchronized void leave(UUID player) {
if (!isRunning()) {
if (game == null) {
if (players.remove(player) != null) {
if (players.size() == 0) {
Lobby.getInstance().removeSession(uuid);
} else {
notifyPlayers(new PlayerLeftMessage(player));
notifyLobby();
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
}
}
} else {
@@ -144,7 +140,7 @@ public class Session implements Observer, SessionMBean {
var player = players.get(uuid);
if (player == null) {
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.");
}
@@ -169,11 +165,15 @@ public class Session implements Observer, SessionMBean {
var player = players.get(uuid);
if (player == null) {
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.");
} else {
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());
} catch (IllegalStateException e) {
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());
messages.add(Pair.of(null, new StartingGameMessage()));
game = new Game(configuration, timeout, 10 * 60 * 1000, this);
notifyLobby();
game = new Game(Configurations.get(configuration).withTimeout(timeout), this);
game.start(List.copyOf(players.keySet()));
CompletableFuture.runAsync(() -> {
while (true) {
@@ -201,23 +200,20 @@ public class Session implements Observer, SessionMBean {
break;
}
}
finishGame();
players.forEach((id, player) -> player.setReady(false));
synchronized (this) {
game = null;
messages.clear();
for (SessionPlayer player : List.copyOf(players.values())) {
if (!player.isConnected()) {
leave(player.getUuid());
}
}
}
});
}
protected synchronized void finishGame() {
players.forEach((id, player) -> player.setReady(false));
game = null;
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);
for (SessionPlayer player : players.values()) {
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()) {
player.send(message);
}
}
public SessionData toData() {
return new SessionData(uuid, name, players.size(), configuration, isRunning());
return new SessionData(uuid, name, players.size(), configuration);
}
@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
@EqualsAndHashCode(of = "uuid")
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.server.NackMessage;
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.server.Lobby;
import eu.jonahbauer.wizard.server.NackException;
import eu.jonahbauer.wizard.server.Session;
import eu.jonahbauer.wizard.server.machine.Player;
import java.io.IOException;
import java.util.UUID;
public class DebugSession extends Session {
@@ -49,7 +52,7 @@ public class DebugSession extends Session {
sessionPlayer.setPlayer(player);
notifyJoined(sessionPlayer.toData());
notifyLobby();
Lobby.getInstance().notifyPlayers(new SessionModifiedMessage(toData()));
return sessionPlayer.getUuid();
}
@@ -57,13 +60,19 @@ public class DebugSession extends Session {
@Override
protected void startGame() {}
public void close() {
for (var sessionPlayer : getPlayers().values()) {
try {
var player = sessionPlayer.getPlayer();
if (player != null) {
player.disconnect();
}
} catch (IOException ignored) {}
}
}
@Override
public void notifyPlayers(ServerMessage message) {
super.notifyPlayers(message);
}
@Override
public boolean isDebug() {
return true;
}
}
@@ -6,7 +6,6 @@ import eu.jonahbauer.wizard.common.messages.server.Response;
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
import eu.jonahbauer.wizard.server.machine.states.CreatedState;
import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
@@ -45,16 +44,8 @@ public class Player extends Context<ClientState, Player> {
shouldBuffer = Response.class;
}
public void disconnect() {
disconnect(null);
}
public void disconnect(@Nullable String reason) {
try {
session.close(new CloseStatus(CloseStatus.GOING_AWAY.getCode(), reason));
} catch (IOException ignored) {
// ignored
}
public void disconnect() throws IOException {
session.close(CloseStatus.SERVER_ERROR);
}
@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();
}