From 6c136b78b8f09e194950fa4ff76ec309283524a1 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 16:22:50 +0100 Subject: [PATCH 01/82] Updated 'Piece' class. Updated the 'Piece' class by reverting all enumeration to its orgin types. --- .../src/main/java/pp/mdga/game/Piece.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java index dfe36188..a01c1247 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java @@ -12,17 +12,17 @@ public class Piece { /** * The shield state of the piece. */ - private int shield; + private ShieldState shield; /** * The current state of the piece. */ - private int state; + private PieceState state; /** * The color of the piece. */ - private final int color; + private final Color color; /** * The unique identifier of the piece. @@ -36,15 +36,15 @@ public class Piece { * @param state the state of the piece */ public Piece(Color color, PieceState state, int id) { - this.color = color.ordinal(); - this.state = state.ordinal(); - shield = ShieldState.NONE.ordinal(); + this.color = color; + this.state = state; + shield = ShieldState.NONE; } private Piece() { - color = Color.NONE.ordinal(); - state = PieceState.WAITING.ordinal(); - shield = ShieldState.NONE.ordinal(); + color = Color.NONE; + state = PieceState.WAITING; + shield = ShieldState.NONE; } /** @@ -53,7 +53,7 @@ private Piece() { * @return the color of the piece */ public void setShield(ShieldState shield) { - this.shield = shield.ordinal(); + this.shield = shield; } /** @@ -62,7 +62,7 @@ public void setShield(ShieldState shield) { * @return the color of the piece */ public ShieldState getShield() { - return ShieldState.values()[shield]; + return shield; } /** @@ -71,7 +71,7 @@ public ShieldState getShield() { * @param state the state of the piece */ public void setState(PieceState state) { - this.state = state.ordinal(); + this.state = state; } /** @@ -80,7 +80,7 @@ public void setState(PieceState state) { * @return the color of the piece */ public PieceState getState() { - return PieceState.values()[state]; + return state; } /** @@ -89,7 +89,7 @@ public PieceState getState() { * @return the color of the piece */ public boolean isShielded() { - return shield == ShieldState.ACTIVE.ordinal(); + return shield == ShieldState.ACTIVE; } /** @@ -98,7 +98,7 @@ public boolean isShielded() { * @return the color of the piece */ public boolean isSuppressed() { - return shield == ShieldState.SUPPRESSED.ordinal(); + return shield == ShieldState.SUPPRESSED; } /** @@ -107,7 +107,7 @@ public boolean isSuppressed() { * @return the color of the piece */ public Color getColor() { - return Color.values()[color]; + return color; } /** From 167d898a3cfaf0c3016f7fccb257dd0035b42d17 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 16:25:33 +0100 Subject: [PATCH 02/82] Updated 'StartNode' class. Updated the 'StartNode' class by reverting all enumerations to its origin types in it. --- .../model/src/main/java/pp/mdga/game/StartNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java index 23161ecf..24b2c267 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java @@ -10,7 +10,7 @@ public class StartNode extends Node { /** * The color of the node. */ - private int color; + private Color color; /** * Creates a new start node with the given color. @@ -18,11 +18,11 @@ public class StartNode extends Node { * @param color the color of the node */ public StartNode(Color color) { - this.color = color.ordinal(); + this.color = color; } private StartNode() { - color = Color.NONE.ordinal(); + color = Color.NONE; } /** @@ -31,7 +31,7 @@ private StartNode() { * @return the color of the node */ public Color getColor() { - return Color.values()[color]; + return color; } /** @@ -40,6 +40,6 @@ public Color getColor() { * @param color the new color of the node */ public void setColor(Color color) { - this.color = color.ordinal(); + this.color = color; } } From 1bcb73cff792b37c49d3411abda1afa1f2002897 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 16:29:32 +0100 Subject: [PATCH 03/82] Updated 'PlayerData' class. Updated the 'PlayerData' class by adding the 'isFinished' method to it. In Addition, the empty constructor was optimized to initalize all class attributes for serializable cases. --- .../main/java/pp/mdga/game/PlayerData.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java index 8fcc85ae..cb2a788b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java @@ -43,10 +43,23 @@ public PlayerData(Color color) { } } + /** + * Constructor. + */ private PlayerData() { - homeNodes = null; - waitingArea = null; - pieces = null; + homeNodes = new HomeNode[4]; + waitingArea = new Piece[4]; + pieces = new Piece[4]; + } + + /** + * This method will be used to check if the player is finished. + * ToDo: Currently return always false. Implement logic! + * + * @return true or false. + */ + public boolean isFinished() { + return false; } /** From cb362c4d0cf028240813ad485254c2f6ae785e0c Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 16:31:57 +0100 Subject: [PATCH 04/82] Used auto-reformate code. --- .../client/src/main/java/pp/mdga/client/MdgaApp.java | 2 +- .../main/java/pp/mdga/server/automaton/LobbyState.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index cb0371c0..2fddea2f 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -1,9 +1,9 @@ package pp.mdga.client; import com.jme3.app.SimpleApplication; +import com.jme3.system.AppSettings; import com.simsilica.lemur.GuiGlobals; import pp.mdga.client.acoustic.AcousticHandler; -import com.jme3.system.AppSettings; import pp.mdga.client.dialog.JoinDialog; import pp.mdga.client.view.*; diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 32b400b6..167992c2 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -49,7 +49,7 @@ public void exit() { * This method will be used to initialize the game and all necessary objects. */ public void initializeGame() { - for (Map.Entry entry: this.logic.getGame().getPlayers().entrySet()) { + for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { this.logic.getGame().getBoard().addPlayerData(entry.getValue().getColor(), new PlayerData(entry.getValue().getColor())); } } @@ -86,7 +86,7 @@ public void received(SelectTSKMessage msg, int from) { } } - if(this.logic.getGame().getPlayerById(from).getColor() != Color.NONE) { + if (this.logic.getGame().getPlayerById(from).getColor() != Color.NONE) { this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, this.logic.getGame().getPlayerById(from).getColor(), false)); } @@ -117,7 +117,7 @@ public void received(DeselectTSKMessage msg, int from) { @Override public void received(LobbyReadyMessage msg, int from) { //assign a free color - if(this.logic.getGame().getPlayerById(from).getColor() == Color.NONE) { + if (this.logic.getGame().getPlayerById(from).getColor() == Color.NONE) { ArrayList colors = new ArrayList<>(); colors.add(Color.ARMY); colors.add(Color.AIRFORCE); @@ -125,12 +125,12 @@ public void received(LobbyReadyMessage msg, int from) { colors.add(Color.CYBER); for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { - if(colors.contains(entry.getValue().getColor())) { + if (colors.contains(entry.getValue().getColor())) { colors.remove(entry.getValue().getColor()); } } - if(colors.size() < 1) { + if (colors.size() < 1) { throw new RuntimeException("can not assign a color"); } From 2118c7289168159b68b63b7650de4986411d9d6c Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 16:34:55 +0100 Subject: [PATCH 05/82] Adjust RollDiceNotification --- .../java/pp/mdga/notification/RollDiceNotification.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java index 13bf849b..14c18e54 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java @@ -11,17 +11,19 @@ public class RollDiceNotification extends Notification{ private int eyes; private boolean turbo; private int multiplier; + private boolean isRanking; /** * Constructor. * @param color the color of the player that rolled the die. * @param eyes the number of eyes that were rolled. */ - public RollDiceNotification(Color color, int eyes) { + public RollDiceNotification(Color color, int eyes, boolean isRanking) { this.color = color; this.eyes = eyes; this.turbo = false; this.multiplier = -1; + this.isRanking = isRanking; } public RollDiceNotification(Color color, int eyes, boolean turbo, int multiplier) { @@ -29,6 +31,7 @@ public RollDiceNotification(Color color, int eyes, boolean turbo, int multiplier this.eyes = eyes; this.turbo = turbo; this.multiplier = multiplier; + this.isRanking = false; } /** @@ -54,4 +57,6 @@ public int getMultiplier() { public boolean isTurbo() { return turbo; } + + public boolean isRanking() { return isRanking; } } From eee6fccde512f29a1b9bd002c8081c8e11716f3f Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 16:35:40 +0100 Subject: [PATCH 06/82] Updated 'DetermineStartPlayerState' class. Updated the 'DetermineStartPlayerState' class by setting the start state correctly and enter it. --- .../pp/mdga/client/gameState/DetermineStartPlayerState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java index cf3c48c2..4b95fe68 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java @@ -25,7 +25,7 @@ public DetermineStartPlayerState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = rollRankingDiceState; + this.setState(this.rollRankingDiceState); } @Override From a2856bb1574634ff7c640e5c1ddf4ed3b95d84e7 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Mon, 2 Dec 2024 16:47:28 +0100 Subject: [PATCH 07/82] added rollRankingResults --- .../pp/mdga/client/InputSynchronizer.java | 7 +++- .../src/main/java/pp/mdga/client/MdgaApp.java | 2 +- .../pp/mdga/client/gui/ActionTextHandler.java | 42 ++++++++++++------- .../java/pp/mdga/client/gui/GuiHandler.java | 5 +++ .../pp/mdga/client/gui/PlayerNameHandler.java | 1 + .../java/pp/mdga/client/view/GameView.java | 8 +++- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index b251134f..1f2d5c10 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -118,8 +118,11 @@ else if(boardSelect != null) { } if(name.equals("Test") &&isPressed){ if(app.getView() instanceof GameView gameView){ -// app.getNotificationSynchronizer().addTestNotification(new FinishNotification(Color.NAVY)); -// app.getNotificationSynchronizer().addTestNotification(new MovePieceNotification()); + gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1); + gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); + gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); + gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4); + } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 5aa92fc0..cb0371c0 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -106,7 +106,7 @@ public void simpleInitApp() { gameView = new GameView(this); ceremonyView = new CeremonyView(this); - enter(MdgaState.GAME); + enter(MdgaState.MAIN); } /** diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java index 4dfa0690..8c8c73b9 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java @@ -10,19 +10,20 @@ import pp.mdga.client.animation.ZoomControl; import pp.mdga.game.Color; -public class ActionTextHandler { + class ActionTextHandler { private Node root; private BitmapFont font; private AppSettings appSettings; + private int ranking; - public ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){ + ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){ root = new Node("actionTextRoot"); guiNode.attachChild(root); root.setLocalTranslation(center(appSettings.getWidth(), appSettings.getHeight(), Vector3f.ZERO)); font = assetManager.loadFont("Fonts/Gunplay.fnt"); this.appSettings = appSettings; - + ranking = 0; } private Node createTextWithSpacing(String[] textArr, float spacing, float size, ColorRGBA[] colorArr) { @@ -74,48 +75,48 @@ private Vector3f centerText(float width, float height, Vector3f pos){ return center(-width, height, pos); } - public void activePlayer(String name, Color color){ + void activePlayer(String name, Color color){ createTopText(new String[]{name," ist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void ownActive(Color color){ + void ownActive(Color color){ createTopText(new String[]{"Du"," bist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void diceNum(int diceNum, String name, Color color){ + void diceNum(int diceNum, String name, Color color){ createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0); createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 100); } - public void diceNumMult(int diceNum,int mult, String name, Color color){ + void diceNumMult(int diceNum,int mult, String name, Color color){ createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0); createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 100); } - public void ownDice(int diceNum){ + void ownDice(int diceNum){ createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 0); } - public void ownDiceMult(int diceNum, int mult){ + void ownDiceMult(int diceNum, int mult){ createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 0); } - public void drawCard(String name, Color color){ + void drawCard(String name, Color color){ createTopText(new String[]{name," erhält eine Bonuskarte"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void drawCardOwn(Color color){ + void drawCardOwn(Color color){ createTopText(new String[]{"Du"," erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void finishText(String name, Color color){ + void finishText(String name, Color color){ createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void finishTextOwn(Color color){ + void finishTextOwn(Color color){ createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } @@ -131,9 +132,22 @@ private ColorRGBA playerColorToColorRGBA(Color color){ }; } - public void hide(){ + void hide(){ + ranking = 0; root.detachAllChildren(); } + float paddingRanked = 100; + + void rollRankingResult(String name, Color color, int eye){ + createTopText(new String[]{name,": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking); + ranking++; + } + + void rollRankingResultOwn(Color color, int eye){ + createTopText(new String[]{"Du",": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking); + ranking++; + } + } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java index 8761e3da..91e091eb 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java @@ -134,5 +134,10 @@ public void finish(Color color){ else actionTextHandler.finishText(playerNameHandler.getName(color), color); } + public void rollRankingResult(Color color, int eye){ + if(ownColor == color) actionTextHandler.rollRankingResultOwn(color, eye); + else actionTextHandler.rollRankingResult(playerNameHandler.getName(color), color, eye); + } + } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java index 1b197d16..2d4bdbf7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java @@ -120,6 +120,7 @@ public void setActivePlayer(Color color) { } public String getName(Color color){ + if(!colorNameMap.containsKey(color)) throw new RuntimeException("color is not in colorNameMap"); return colorNameMap.get(color); } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java index 9d1a7fe5..f28719ac 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java @@ -65,8 +65,12 @@ public void onEnter() { app.getAcousticHandler().playSound(MdgaSound.START); - app.getNotificationSynchronizer().addTestNotification(new AcquireCardNotification(BonusCard.SHIELD)); - app.getNotificationSynchronizer().addTestNotification(new SelectableCardsNotification(List.of(BonusCard.SHIELD))); +// guiHandler.addPlayer(Color.AIRFORCE, "Cedric"); +// guiHandler.addPlayer(Color.ARMY, "Ben"); +// guiHandler.addPlayer(Color.CYBER, "Felix"); +// guiHandler.addPlayer(Color.NAVY, "Daniel"); + + } @Override From 0411f2ead41f03a5cfdefb11bfb0829b58b15557 Mon Sep 17 00:00:00 2001 From: Hanno Fleischer Date: Mon, 2 Dec 2024 17:08:46 +0100 Subject: [PATCH 08/82] fixed state transitions in gamestateclient automaton --- .../src/main/java/pp/mdga/client/CeremonyState.java | 6 ++++-- .../src/main/java/pp/mdga/client/DialogsState.java | 11 ++++------- .../model/src/main/java/pp/mdga/client/GameState.java | 9 +++++---- .../java/pp/mdga/client/dialogState/LobbyState.java | 2 +- .../client/gameState/DetermineStartPlayerState.java | 4 +++- .../main/java/pp/mdga/client/gameState/TurnState.java | 6 ++++-- .../RollRankingDiceState.java | 4 +++- .../client/gameState/turnState/ChoosePieceState.java | 8 +++++--- .../client/gameState/turnState/PowerCardState.java | 6 ++++-- 9 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java index 38541512..cf6e5e5a 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java @@ -18,7 +18,7 @@ public CeremonyState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - currentState = podiumState; + setState(podiumState); logic.addNotification(createCeremonyNotification()); } @@ -28,7 +28,9 @@ public void exit() { } public void setState(CeremonyStates state){ - this.currentState.exit(); + if(this.currentState != null){ + this.currentState.exit(); + } state.enter(); currentState = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index c3f33c97..c8578cd3 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -30,13 +30,15 @@ public void exit(){ @Override public void enter(){ - currentState = startDialogState; + setState(startDialogState); ownPlayerID = 0; ownPlayerName = null; } public void setState(DialogStates newState){ - currentState.exit(); + if(currentState != null){ + currentState.exit(); + } newState.enter(); currentState = newState; } @@ -69,11 +71,6 @@ public StartDialogState getStartDialog() { return startDialogState; } - public void startGame(){ - exit(); - logic.setState(logic.getGameState()); - } - @Override public void selectLeave(){ currentState.selectLeave(); diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java index af449db8..31b3f340 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java @@ -27,7 +27,6 @@ public class GameState extends ClientState { */ public GameState(ClientState parent, ClientGameLogic logic) { super(parent, logic); - state = determineStartPlayerState; } /** @@ -35,7 +34,7 @@ public GameState(ClientState parent, ClientGameLogic logic) { */ @Override public void enter() { - + this.setState(this.determineStartPlayerState); } /** @@ -52,8 +51,10 @@ public void exit() { * @param newState the state to be set */ public void setState(GameStates newState){ - state.exit(); - state.enter(); + if(this.state != null){ + this.state.exit(); + } + newState.enter(); state = newState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index 6d70ba50..2d48cbe1 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -95,7 +95,7 @@ public void received(ServerStartGameMessage msg){ } logic.addNotification(new PlayerInGameNotification(entry.getKey(), pieceList , logic.getGame().getPlayerByColor(entry.getKey()).getName())); } - parent.startGame(); + logic.setState(logic.getGameState()); } @Override diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java index 4b95fe68..4794785a 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java @@ -34,7 +34,9 @@ public void exit() { } public void setState(DetermineStartPlayerStates state) { - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java index b59a35b8..5f075eff 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java @@ -31,7 +31,7 @@ public TurnState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = powerCardState; + this.setState(this.powerCardState); } @Override @@ -40,7 +40,9 @@ public void exit() { } public void setState(TurnStates state){ - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java index 3c1ec5b9..e6a98522 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java @@ -6,6 +6,7 @@ import pp.mdga.message.client.RequestDieMessage; import pp.mdga.message.server.DieMessage; import pp.mdga.notification.DiceNowNotification; +import pp.mdga.notification.RollDiceNotification; public class RollRankingDiceState extends DetermineStartPlayerStates { @@ -23,16 +24,17 @@ public void enter() { @Override public void exit() { - } @Override public void selectDice(){ + System.out.println("selectDice"); logic.send(new RequestDieMessage()); } @Override public void received(DieMessage msg){ + logic.addNotification(new RollDiceNotification(logic.getGame().getPlayerById(logic.getDialogs().getOwnPlayerId()).getColor(), msg.getDiceEye(),true)); parent.setState(parent.getWaitRanking()); } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java index b1a0c7e3..47dce439 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java @@ -24,7 +24,7 @@ public ChoosePieceState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - currentState = noPieceState; + this.setState(this.noPieceState); } @Override @@ -34,9 +34,11 @@ public void exit() { } public void setState(ChoosePieceStates state){ - currentState.exit(); + if(currentState != null){ + currentState.exit(); + } + state.enter(); currentState = state; - currentState.enter(); } @Override diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java index fd80b68d..5a5fd5c3 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java @@ -31,7 +31,7 @@ public PowerCardState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = choosePowerCardState; + this.setState(this.choosePowerCardState); } public void exit() { @@ -40,7 +40,9 @@ public void exit() { } public void setState(PowerCardStates state) { - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } From 208261c6bfc27a6e394bca85270ff100a88a0ec5 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Mon, 2 Dec 2024 17:11:55 +0100 Subject: [PATCH 09/82] added clickDice Action + modelSync --- .../java/pp/mdga/client/InputSynchronizer.java | 18 ++++++++++++------ .../pp/mdga/client/gui/ActionTextHandler.java | 5 ++++- .../java/pp/mdga/client/gui/GuiHandler.java | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index 1f2d5c10..4e22978d 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -17,6 +17,7 @@ import pp.mdga.client.board.OutlineControl; import pp.mdga.client.board.PieceControl; import pp.mdga.client.gui.CardControl; +import pp.mdga.client.gui.DiceControl; import pp.mdga.client.view.GameView; import pp.mdga.game.BonusCard; import pp.mdga.game.Color; @@ -93,10 +94,15 @@ public void onAction(String name, boolean isPressed, float tpf) { } if(name.equals("Click") && isPressed) { if (app.getView() instanceof GameView gameView) { + DiceControl diceSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), DiceControl.class); CardControl cardLayerSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), CardControl.class); OutlineControl boardSelect = checkHover(app.getCamera(), app.getRootNode(), OutlineControl.class); - if(cardLayerSelect != null) { + if(diceSelect != null) { + app.getModelSynchronize().rolledDice(); + gameView.getGuiHandler().hideText(); + } + else if(cardLayerSelect != null) { //cardSelect if(cardLayerSelect.isSelectable()) gameView.getGuiHandler().selectCard(cardLayerSelect); } @@ -118,11 +124,11 @@ else if(boardSelect != null) { } if(name.equals("Test") &&isPressed){ if(app.getView() instanceof GameView gameView){ - gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1); - gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); - gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); - gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4); - +// gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1); +// gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); +// gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); +// gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4); + gameView.getGuiHandler().showDice(); } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java index 8c8c73b9..b5bd3571 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java @@ -134,7 +134,7 @@ private ColorRGBA playerColorToColorRGBA(Color color){ void hide(){ ranking = 0; - root.detachAllChildren(); + root.detachAllChildren(); } float paddingRanked = 100; @@ -149,5 +149,8 @@ void rollRankingResultOwn(Color color, int eye){ ranking++; } + void diceNow(){ + createTopText("Klicke zum Würfeln", 5, 80, ColorRGBA.White, 0); + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java index 91e091eb..d4128081 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java @@ -67,6 +67,7 @@ public void showRolledDice(int rollNum, Color color) { public void showDice() { cardLayerHandler.showDice(); + actionTextHandler.diceNow(); } public void hideDice() { From f21fd9b0a66aca701dd2c8232a7fae9390d0d379 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Mon, 2 Dec 2024 17:28:37 +0100 Subject: [PATCH 10/82] added setPauseOnLostFocus(false) --- .../src/main/java/pp/mdga/client/InputSynchronizer.java | 1 - .../mdga/client/src/main/java/pp/mdga/client/MdgaApp.java | 1 + .../java/pp/mdga/client/NotificationSynchronizer.java | 1 + .../java/pp/mdga/notification/RollDiceNotification.java | 8 ++++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index 4e22978d..807a37c4 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -100,7 +100,6 @@ public void onAction(String name, boolean isPressed, float tpf) { if(diceSelect != null) { app.getModelSynchronize().rolledDice(); - gameView.getGuiHandler().hideText(); } else if(cardLayerSelect != null) { //cardSelect diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 2fddea2f..9eca1ab8 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -82,6 +82,7 @@ public static void main(String[] args) { MdgaApp app = new MdgaApp(); app.setSettings(settings); app.setShowSettings(false); + app.setPauseOnLostFocus(false); app.start(); } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 6e080158..f23eda0e 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -148,6 +148,7 @@ private void handleGame(Notification notification) { } else if (notification instanceof ResumeNotification) { //TODO } else if (notification instanceof RollDiceNotification n) { + gameView.getGuiHandler().hideText(); if(n.getColor() == gameView.getOwnColor()){ guiHandler.rollDice(n.getEyes(), n.isTurbo() ? n.getMultiplier() : -1); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java index 14c18e54..2e6f03e1 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java @@ -18,6 +18,14 @@ public class RollDiceNotification extends Notification{ * @param color the color of the player that rolled the die. * @param eyes the number of eyes that were rolled. */ + public RollDiceNotification(Color color, int eyes) { + this.color = color; + this.eyes = eyes; + this.turbo = false; + this.multiplier = -1; + this.isRanking = false; + } + public RollDiceNotification(Color color, int eyes, boolean isRanking) { this.color = color; this.eyes = eyes; From 09fda6b1675d36789c6ab598f7e67ef10682ccf2 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 18:18:04 +0100 Subject: [PATCH 11/82] Updated 'Game' class. Updated the 'Game' class by adding the 'isColorTaken' method to it. --- .../model/src/main/java/pp/mdga/game/Game.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 666f1b8b..69bab269 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -96,6 +96,23 @@ public void updatePlayerActiveState(int id, boolean active) { this.players.get(id).setActive(active); } + /** + * This method will be used to check if the given color parameter is already taken. + * If yes it will return true, otherwise false. + * + * @param color as the color which should be checked if taken as a Color enumeration. + * @return true or false. + */ + public boolean isColorTaken(Color color) { + for (Map.Entry entry : this.players.entrySet()) { + if (entry.getValue().getColor() == color) { + return true; + } + } + + return false; + } + /** * This method will be used to return the player which has the given id parameter. * From 3daafde9f182c2c67bbc4758516fa6a0ffaed343 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 18:50:42 +0100 Subject: [PATCH 12/82] Updated 'ServerInterpreter' class. Updated the 'ServerInterpreter' class by adding the 'received(IncorrectRequestMessage msg)' method to it. --- .../pp/mdga/message/server/ServerInterpreter.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java index 4491f9c1..edb54c9a 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java @@ -211,11 +211,14 @@ public interface ServerInterpreter { /** * Handles a SelectTSK message received from the server. * - * @param shutdownMessage the SelectTSK message received. + * @param msg the SelectTSK message received. */ - void received(ShutdownMessage shutdownMessage); + void received(ShutdownMessage msg); - void received(StartBriefingMessage msg); - - void received(PlayerDataMessage msg); + /** + * Handles a IncorrectRequest message received from the server. + * + * @param msg the IncorrectRequest message received. + */ + void received(IncorrectRequestMessage msg); } From 347ed152b84209725c972820b00a1efd9505dec7 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 18:58:15 +0100 Subject: [PATCH 13/82] Added 'IncorrectRequestMessage' class. Added the 'IncorrectRequestMessage' class to this project. It will be used to send the client an incorrect request message to show they did something wrong. --- .../server/IncorrectRequestMessage.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java new file mode 100644 index 00000000..eeae3717 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java @@ -0,0 +1,58 @@ +package pp.mdga.message.server; + +import com.jme3.network.serializing.Serializable; + +@Serializable +public class IncorrectRequestMessage extends ServerMessage { + /** + * Create IncorrectRequestMessage attributes. + */ + private final int id; + + /** + * Constructor. + * + * @param id as the id of the error message as an Integer. + */ + public IncorrectRequestMessage(int id) { + this.id = id; + } + + /** + * Constructor. + */ + public IncorrectRequestMessage() { + this.id = 0; + } + + /** + * Accepts a visitor to process this message. + * + * @param interpreter the visitor to process this message + */ + @Override + public void accept(ServerInterpreter interpreter) { + interpreter.process(this); + } + + /** + * Gets the bundle key of the informational text to be shown at the client. + * This key is used to retrieve the appropriate localized text for display. + * + * @return the bundle key of the informational text + */ + @Override + public String getInfoTextKey() { + return ""; + } + + /** + * This method will be used to return necessary class information in a more readable format. + * + * @return information as a String. + */ + @Override + public String toString() { + return "IncorrectRequestMessage with id: %d".formatted(this.id); + } +} From 294ecdc56fd23c4b3cce30408b34a83dcd9663c1 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 19:00:21 +0100 Subject: [PATCH 14/82] Updated 'IncorrectRequestMessage' class. Updated the 'IncorrectRequestMessage' class by updating the content inside the 'accept' method in it. --- .../java/pp/mdga/message/server/IncorrectRequestMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java index eeae3717..df51705b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/IncorrectRequestMessage.java @@ -32,7 +32,7 @@ public IncorrectRequestMessage() { */ @Override public void accept(ServerInterpreter interpreter) { - interpreter.process(this); + interpreter.received(this); } /** From 90a21087df0288852dfdeafa2dd48f3045ab2f5c Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 19:02:00 +0100 Subject: [PATCH 15/82] added logic for incorrectRequest message and removed messages playerdata and startbriefing and created javadocs --- .../src/main/java/pp/mdga/client/MdgaApp.java | 2 +- .../java/pp/mdga/client/CeremonyState.java | 32 ++ .../java/pp/mdga/client/ClientGameLogic.java | 312 +++++++++++++++++- .../main/java/pp/mdga/client/ClientState.java | 13 +- .../java/pp/mdga/client/DialogsState.java | 128 ++++++- .../main/java/pp/mdga/client/GameState.java | 131 ++++++++ .../java/pp/mdga/client/SettingsState.java | 9 + 7 files changed, 613 insertions(+), 14 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 9eca1ab8..41692841 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -1,9 +1,9 @@ package pp.mdga.client; import com.jme3.app.SimpleApplication; -import com.jme3.system.AppSettings; import com.simsilica.lemur.GuiGlobals; import pp.mdga.client.acoustic.AcousticHandler; +import com.jme3.system.AppSettings; import pp.mdga.client.dialog.JoinDialog; import pp.mdga.client.view.*; diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java index cf6e5e5a..ae64890b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java @@ -12,21 +12,38 @@ public class CeremonyState extends ClientState { private final PodiumState podiumState = new PodiumState(this, logic); private final StatisticsState statisticsState = new StatisticsState(this, logic); + /** + * Creates a new CeremonyState + * + * @param parent the parent state + * @param logic the game logic + */ public CeremonyState(ClientState parent, ClientGameLogic logic) { super(parent, logic); } + /** + * Enters the new state machine + */ @Override public void enter() { setState(podiumState); logic.addNotification(createCeremonyNotification()); } + /** + * exits this state + */ @Override public void exit() { currentState.exit(); } + /** + * This method is used to set a new SubState + * + * @param state the state to be set + */ public void setState(CeremonyStates state){ if(this.currentState != null){ this.currentState.exit(); @@ -35,14 +52,29 @@ public void setState(CeremonyStates state){ currentState = state; } + /** + * This method get the PodiumState + * + * @return the PodiumState + */ public PodiumState getPodiumState(){ return podiumState; } + /** + * This method get the StatisticsState + * + * @return the StatisticsState + */ public StatisticsState getStatisticsState(){ return statisticsState; } + /** + * This method is used to get the current State + * + * @return the current State + */ public CeremonyStates getState(){ return currentState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java index f3a0602b..e27a7d49 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java @@ -13,6 +13,10 @@ import java.util.Map; import java.util.UUID; +/** + * The ClientGameLogic class is the main class for the client side of the game. + * It is responsible for handling the game logic on the client side. + */ public class ClientGameLogic implements ServerInterpreter { static final System.Logger LOGGER = System.getLogger(ClientGameLogic.class.getName()); @@ -28,6 +32,11 @@ public class ClientGameLogic implements ServerInterpreter { private final InterruptState interruptState = new InterruptState(null, this); private final SettingsState settingsState = new SettingsState(null, this); + /** + * Creates a new ClientGameLogic + * + * @param clientSender the client sender + */ public ClientGameLogic(ClientSender clientSender) { this.game = new Game(); this.clientSender = clientSender; @@ -35,11 +44,21 @@ public ClientGameLogic(ClientSender clientSender) { state = dialogsState; } + /** + * This method is used to send a message to the server + * + * @param msg the message to be sent + */ public void send(ClientMessage msg){ LOGGER.log(System.Logger.Level.INFO, "send {0}", msg); clientSender.send(msg); } + /** + * This method is used to get a piece by its id + * @param pieceId the UUID of the piece + * @return the piece + */ private Piece getPiece(UUID pieceId){ for(Map.Entry entry : game.getBoard().getPlayerData().entrySet()){ for(Piece piece : entry.getValue().getPieces()){ @@ -51,216 +70,432 @@ private Piece getPiece(UUID pieceId){ return null; } + /** + * This method returns the clientSender + * + * @return the clientSender + */ public ClientSender getClientSender(){ return clientSender; } + /** + * This method returns the game + * + * @return the game + */ public Game getGame(){ return game; } + /** + * This method returns the current State + * + * @return the current State + */ public ClientState getState(){ return state; } + /** + * This method returns if the client is a host + * + * @return if the client is a host + */ public boolean isHost(){ return isHost; } + /** + * This method returns the steps you can calculate steps + * + * @return the calculated moves as int + */ public int getCalculatedMoves(){ return game.getDiceEyes() * game.getDiceModifier(); } + /** + * This method sets if the player is a host + * + * @param isHost the boolean value + */ public void setHost(boolean isHost){ this.isHost = isHost; } + /** + * This method calls the method received of the state + * + * @param msg the ActivePlayer message received + */ @Override public void received(ActivePlayerMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the AnyPiece message received + */ @Override public void received(AnyPieceMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the Briefing message received + */ @Override public void received(BriefingMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the Ceremony message received + */ @Override public void received(CeremonyMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the Dice message received + */ @Override public void received(DieMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the DiceAgain message received + */ @Override public void received(DiceAgainMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the DiceNow message received + */ @Override public void received(DiceNowMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the EndOfGame message received + */ @Override public void received(EndOfTurnMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the GameOver message received + */ @Override public void received(LobbyAcceptMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the LobbyDeny message received + */ @Override public void received(LobbyDenyMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the LobbyPlayerJoin message received + */ @Override public void received(LobbyPlayerJoinedMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the LobbyPlayerLeave message received + */ @Override public void received(LobbyPlayerLeaveMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the MoveMessage message received + */ @Override public void received(MoveMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the NoTurn message received + */ @Override public void received(NoTurnMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the PauseGame message received + */ @Override public void received(PauseGameMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the PlayCard message received + */ @Override public void received(PlayCardMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the PossibleCard message received + */ @Override public void received(PossibleCardMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the PossiblePiece message received + */ @Override public void received(PossiblePieceMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the RankingResponse message received + */ @Override public void received(RankingResponseMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the RankingRollAgain message received + */ @Override public void received(RankingRollAgainMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the ReconnectBriefing message received + */ @Override public void received(ReconnectBriefingMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the ResumeGame message received + */ @Override public void received(ResumeGameMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the ServerStartGame message received + */ @Override public void received(ServerStartGameMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the SelectTSK message received. + */ @Override public void received(ShutdownMessage msg) {state.received(msg);} + /** + * Handles a IncorrectRequest message received from the server. + * + * @param msg the IncorrectRequest message received. + */ @Override - public void received(StartBriefingMessage msg) { - state.received(msg); - } - - @Override - public void received(PlayerDataMessage msg) { + public void received(IncorrectRequestMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the StartPiece message received + */ @Override public void received(StartPieceMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the UpdateReady message received + */ @Override public void received(UpdateReadyMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the UpdateTSK message received + */ @Override public void received(UpdateTSKMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the WaitPiece message received + */ @Override public void received(WaitPieceMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the Spectator message received. + */ @Override public void received(SpectatorMessage msg) { state.received(msg); } + /** + * This method calls the method received of the state + * + * @param msg the SelectPiece message received. + */ @Override public void received(SelectPieceMessage msg) { state.received(msg); } + /** + * This method calls the method selectPiece + * + * @param pieceId the pieceID + */ public void selectPiece(UUID pieceId){ state.selectPiece(getPiece(pieceId)); } + /** + * This method call the method selectNExt of the state + */ public void selectNext(){ state.selectNext(); } + /** + * This method calls the method selectCard of the state + * + * @param card the BonusCard to selected + */ public void selectCard(BonusCard card){ state.selectCard(card); } + /** + * This method call the method selectTsk of the state + * + * @param color the Color to be selected + */ public void selectTsk(Color color){ state.selectTSK(color); } + /** + * The method calls the method deselectTsk of the state + * + * @param color the color to be deselcted + */ public void deselectTSK(Color color){ state.deselectTSK(color); } + /** + * This method calls the selectDice method of the state + */ public void selectDice(){ state.selectDice(); } + /** + * This method calls the selectName method of the state + * + * @param name the name to be set + */ public void selectName(String name){ state.setName(name); } + /** + * This method calls a method of the state base on the parameter value + * + * @param ready the value if this method should ready or unready + */ public void selectReady(boolean ready){ if(ready){ state.selectReady(); @@ -269,62 +504,122 @@ public void selectReady(boolean ready){ } } + /** + * This method calls the selectHost method of the state + * + * @param name the name of the player hosting + */ public void selectHost(String name){ state.selectHost(name); } + /** + * This method calls the selectLeave method of the state + */ public void selectLeave(){ state.selectLeave(); } + /** + * This method calls the selectJoin method of the state + * + * @param ip the ip to cennect to + */ public void selectJoin(String ip){ state.selectJoin(ip); } + /** + * This method calls the selectAnimationEnd method of the state + */ public void selectAnimationEnd(){ state.selectAnimationEnd(); } + /** + * This method calls the selectStart method of the state + */ public void selectStart(){ state.selectStart(); } + /** + * This method calls the selectResume method of the state + */ public void selectResume(){ state.selectResume(); } + /** + * This method is used to transition between states + * + * @param state the new state + */ public void setState(ClientState state){ this.state.exit(); state.enter(); this.state = state; } + /** + * This method is used to enter the interrupt state and save the previous state + */ public void enterInterrupt(){ interruptState.enter(); interruptState.setPreviousState(state); this.state = interruptState; } + /** + * This method is used to get the GameState + * + * @return the GameState + */ public GameState getGameState(){ return gameState; } + /** + * This method is used to get the CeremonyState + * + * @return the CeremonyState + */ public CeremonyState getCeremony(){ return ceremonyState; } + /** + * This method is used to get the InterruptState + * + * @return the InterruptState + */ public InterruptState getInterrupt(){ return interruptState; } + /** + * This method is used to get the DialogsState + * + * @return the DialogsState + */ public DialogsState getDialogs(){ return dialogsState; } + /** + * This method is used to get the SettingsState + * + * @return the SettingsState + */ public SettingsState getSettings(){ return settingsState; } + /** + * This method is used to get the next notification + * + * @return the next notification + */ public Notification getNotification(){ if(!notifications.isEmpty()){ return notifications.remove(0); @@ -333,6 +628,11 @@ public Notification getNotification(){ } } + /** + * This method is used to add a notification + * + * @param notification the notification to be added + */ public void addNotification(Notification notification){ notifications.add(notification); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java index ae500c42..12559a93 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java @@ -184,12 +184,8 @@ public void received(WaitPieceMessage msg) { } @Override - public void received(StartBriefingMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); - } - - public void received(PlayerDataMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + public void received(IncorrectRequestMessage msg) { + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); } public void selectPiece(Piece piece) { @@ -252,6 +248,11 @@ public void selectResume(){ LOGGER.log(Level.DEBUG, "Resume not allowed"); } + /** + * This method is used to create a CeremonyNotification + * + * @return the created CeremonyNotification + */ protected CeremonyNotification createCeremonyNotification(){ CeremonyNotification notification = new CeremonyNotification(); for (var player : logic.getGame().getPlayers().entrySet()){ diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index c8578cd3..38b55ae6 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -18,16 +18,27 @@ public class DialogsState extends ClientState { private final NetworkDialogState networkDialogState = new NetworkDialogState(this, logic); private final StartDialogState startDialogState = new StartDialogState(this, logic); - + /** + * Creates a new DialogsState + * + * @param parent the parent state + * @param logic the game logic + */ public DialogsState(ClientState parent, ClientGameLogic logic) { super(parent, logic); } + /** + * exits this state + */ @Override public void exit(){ currentState.exit(); } + /** + * Enters the new state machine + */ @Override public void enter(){ setState(startDialogState); @@ -35,6 +46,11 @@ public void enter(){ ownPlayerName = null; } + /** + * This method is used to set a new SubState + * + * @param newState the state to be set + */ public void setState(DialogStates newState){ if(currentState != null){ currentState.exit(); @@ -43,114 +59,224 @@ public void setState(DialogStates newState){ currentState = newState; } + /** + * This method is used to get the ownPlayerId + * + * @return the ownPlayerId + */ public int getOwnPlayerId() { return ownPlayerID; } + /** + * This method is used to get the ownPlayerName + * + * @return the ownPlayerName + */ public String getOwnPlayerName() { return ownPlayerName; } + /** + * This method is used to set the ownPlayerName + * + * @param ownPlayerName the ownPlayerName to be set + */ public void setOwnPlayerName(String ownPlayerName) { this.ownPlayerName = ownPlayerName; } + /** + * This method is used to set the ownPlayerId + * + * @param ownPlayerId the ownPlayerId to be set + */ public void setOwnPlayerId(int ownPlayerId) { this.ownPlayerID = ownPlayerId; } + /** + * This method is used to get the lobbyState + * + * @return the lobbyState + */ public LobbyState getLobby() { return lobbyState; } + /** + * This method is used to get the networkDialogState + * + * @return the networkDialogState + */ public NetworkDialogState getNetworkDialog() { return networkDialogState; } + /** + * This method is used to get the startDialogState + * + * @return the startDialogState + */ public StartDialogState getStartDialog() { return startDialogState; } + /** + * This method is used to call the selectLeave method of the current state + */ @Override public void selectLeave(){ currentState.selectLeave(); } + /** + * This method is used to call the selectName method of the current state + * + * @param name the name to be set + */ @Override public void setName(String name){ currentState.setName(name); } + /** + * This method is used to call the selectTSK method of the current state + * + * @param color the color to be set + */ @Override public void selectTSK(Color color){ currentState.selectTSK(color); } + /** + * This method is used to call the deselectTSK method of the current state + * + * @param color the color to be deselected + */ @Override public void deselectTSK(Color color){ currentState.deselectTSK(color); } + /** + * This method is used to call the selectReady method of the current state + */ @Override public void selectReady(){ currentState.selectReady(); } + /** + * This method is used to call the selectUnready method of the current state + */ @Override public void selectUnready(){ currentState.selectUnready(); } + /** + * This method is used to call the selectStart method of the current state + */ @Override public void selectStart(){ currentState.selectStart(); } + /** + * This method is used to call the selectJoin method of the current state + * + * @param string the string to be set + */ @Override public void selectJoin(String string){ currentState.selectJoin(string); } + /** + * This method is used to call the selectHost method of the current state + * + * @param name the name to be set + */ @Override public void selectHost(String name){ currentState.selectHost(name); } + /** + * This method is used to call the received method of the current state + * + * @param msg the LobbyPlayerJoin message received + */ @Override public void received(LobbyPlayerJoinedMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the LobbyPlayerLeave message received + */ @Override public void received(LobbyPlayerLeaveMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the UpdateTSKMessage message received + */ @Override public void received(UpdateTSKMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the UpdateReady message received + */ @Override public void received(UpdateReadyMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the ServerStartGame message received + */ @Override public void received(ServerStartGameMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the ServerStartGame message received + */ @Override public void received(PlayerDataMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the StartBriefing message received + */ @Override public void received(StartBriefingMessage msg){ currentState.received(msg); } + /** + * This method is used to get the current state + */ public DialogStates getState() { return currentState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java index 31b3f340..48da8a6c 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java @@ -58,132 +58,263 @@ public void setState(GameStates newState){ state = newState; } + /** + * This method is used to call the selectAnimationEnd method of the current state + */ @Override public void selectAnimationEnd(){ state.selectAnimationEnd(); } + /** + * This method is used to call the selectDice method of the current state + */ @Override public void selectDice(){ state.selectDice(); } + /** + * This method is used to call the selectPiece method of the current state + * + * @param piece the piece to be selected + */ @Override public void selectPiece(Piece piece){ state.selectPiece(piece); } + /** + * This method is used to call the selectCard method of the current state + * + * @param card the card to be selected + */ @Override public void selectCard(BonusCard card){ state.selectCard(card); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(PauseGameMessage msg){ logic.enterInterrupt(); logic.addNotification(new InterruptNotification(logic.getGame().getPlayers().get(msg.getPlayerId()).getColor())); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(DieMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(RankingRollAgainMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(RankingResponseMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(SelectPieceMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(WaitPieceMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(StartPieceMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(NoTurnMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(MoveMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(CeremonyMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(EndOfTurnMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(SpectatorMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(DiceAgainMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(PossibleCardMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(PlayCardMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(DiceNowMessage msg){ state.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the message to be received + */ @Override public void received(ActivePlayerMessage msg){ state.received(msg); } + /** + * This method returns the current state + * + * @return the current state + */ public GameStates getState(){ return state; } + /** + * This method returns the AnimationState + * + * @return the AnimationState + */ public AnimationState getAnimation() { return animationState; } + /** + * This method returns the DetermineStartPlayerState + * + * @return the DetermineStartPlayerState + */ public DetermineStartPlayerState getDetermineStartPlayer() { return determineStartPlayerState; } + /** + * This method returns the SpectatorState + * + * @return the SpectatorState + */ public SpectatorState getSpectator() { return spectatorState; } + /** + * This method returns the TurnState + * + * @return the TurnState + */ public TurnState getTurn() { return turnState; } + /** + * This method returns the WaitingState + * + * @return the WaitingState + */ public WaitingState getWaiting() { return waitingState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/SettingsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/SettingsState.java index 5c399750..343d974b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/SettingsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/SettingsState.java @@ -49,14 +49,23 @@ public SettingStates getState(){ return currentState; } + /** + * Returns the main settings state + */ public MainSettingsState getMainSettingsState(){ return mainSettingsState; } + /** + * Returns the audio settings state + */ public AudioSettingsState getAudioSettingsState(){ return audioSettingsState; } + /** + * Returns the video settings state + */ public VideoSettingsState getVideoSettingsState(){ return videoSettingsState; } From 0a0762b6c9f131dc72f65d83251c564a1714f267 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 19:07:58 +0100 Subject: [PATCH 16/82] removed all instances of PlayerDataMEssage and StartBriefingMessage --- .../java/pp/mdga/client/DialogsState.java | 20 ------------------- .../mdga/client/dialogState/LobbyState.java | 12 ----------- 2 files changed, 32 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index 38b55ae6..c6f28994 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -254,26 +254,6 @@ public void received(ServerStartGameMessage msg){ currentState.received(msg); } - /** - * This method is used to call the received method of the current state - * - * @param msg the ServerStartGame message received - */ - @Override - public void received(PlayerDataMessage msg){ - currentState.received(msg); - } - - /** - * This method is used to call the received method of the current state - * - * @param msg the StartBriefing message received - */ - @Override - public void received(StartBriefingMessage msg){ - currentState.received(msg); - } - /** * This method is used to get the current state */ diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index 2d48cbe1..dcbe16f2 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -5,14 +5,11 @@ import pp.mdga.client.DialogsState; import pp.mdga.game.Color; import pp.mdga.game.Piece; -import pp.mdga.game.Player; import pp.mdga.game.PlayerData; import pp.mdga.message.client.*; import pp.mdga.message.server.LobbyPlayerJoinedMessage; import pp.mdga.message.server.LobbyPlayerLeaveMessage; -import pp.mdga.message.server.PlayerDataMessage; import pp.mdga.message.server.ServerStartGameMessage; -import pp.mdga.message.server.StartBriefingMessage; import pp.mdga.message.server.UpdateReadyMessage; import pp.mdga.message.server.UpdateTSKMessage; import pp.mdga.notification.*; @@ -75,15 +72,6 @@ public void selectStart(){ } } - @Override - public void received(StartBriefingMessage msg){ - logic.getGame().setBoard(msg.getBoard()); - } - - public void received(PlayerDataMessage msg){ - logic.getGame().getBoard().addPlayerData(msg.getColor(), msg.getPlayerData()); - } - @Override public void received(ServerStartGameMessage msg){ logic.addNotification(new GameNotification(logic.getGame().getPlayerById(parent.getOwnPlayerId()).getColor())); From 289158cf35d49b8f3687eb3695ee353e1ec83a48 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 19:14:11 +0100 Subject: [PATCH 17/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by removing the 'PlayerDataMessage' and 'StartBriefingMessage' from the serializer. --- .../client/src/main/java/pp/mdga/client/server/MdgaServer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index b8e8bfb4..20264ef9 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -2,7 +2,6 @@ import com.jme3.network.*; import com.jme3.network.serializing.Serializer; -import com.jme3.network.serializing.serializers.EnumSerializer; import pp.mdga.game.*; import pp.mdga.message.client.*; import pp.mdga.message.server.*; @@ -144,8 +143,6 @@ private void initializeSerializables() { Serializer.registerClass(StartNode.class); Serializer.registerClass(PlayerData.class); Serializer.registerClass(HomeNode.class); - Serializer.registerClass(PlayerDataMessage.class); - Serializer.registerClass(StartBriefingMessage.class); } private void registerListeners() { From 0d9a922f55f7ead1351f729bc12d7fe86b97adf9 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 19:16:01 +0100 Subject: [PATCH 18/82] Removed 'PlayerDataMessage' and 'StartBriefingMessage'. --- .../message/server/PlayerDataMessage.java | 40 ------------------- .../message/server/StartBriefingMessage.java | 33 --------------- 2 files changed, 73 deletions(-) delete mode 100644 Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java delete mode 100644 Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java deleted file mode 100644 index 03171e73..00000000 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -package pp.mdga.message.server; - -import com.jme3.network.serializing.Serializable; -import pp.mdga.game.Color; -import pp.mdga.game.PlayerData; - -@Serializable -public class PlayerDataMessage extends ServerMessage{ - - private final PlayerData playerData; - private final Color color; - - public PlayerDataMessage(PlayerData playerData, Color color){ - super(); - this.playerData = playerData; - this.color = color; - } - - private PlayerDataMessage(){ - this(null, null); - } - - @Override - public void accept(ServerInterpreter interpreter) { - interpreter.received(this); - } - - @Override - public String getInfoTextKey() { - return ""; - } - - public PlayerData getPlayerData(){ - return playerData; - } - - public Color getColor(){ - return color; - } -} diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java deleted file mode 100644 index ba465990..00000000 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -package pp.mdga.message.server; - -import com.jme3.network.serializing.Serializable; -import pp.mdga.game.Board; - -@Serializable -public class StartBriefingMessage extends ServerMessage { - - private final Board board; - - public StartBriefingMessage(Board board) { - super(); - this.board = board; - } - - private StartBriefingMessage() { - this(null); - } - - public Board getBoard() { - return this.board; - } - - @Override - public void accept(ServerInterpreter interpreter) { - interpreter.received(this); - } - - @Override - public String getInfoTextKey() { - return ""; - } -} From 44f893ccefca5de3b22d24b1a41541776e7907b1 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 19:45:11 +0100 Subject: [PATCH 19/82] Updated 'Game' class. Updated the 'Game' class by setting the 'die' attribute correctly inside the constructor. --- Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 69bab269..4c27d8f2 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -65,6 +65,7 @@ public Game() { drawPile.add(BonusCard.SHIELD); } board = new Board(); + die = new Die(); } /** From 005df941149846872e70e1861c482ee6e13af637 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 19:55:18 +0100 Subject: [PATCH 20/82] added Resources calss to access teh properties --- .../src/main/java/pp/mdga/Resources.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Projekte/mdga/model/src/main/java/pp/mdga/Resources.java diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java new file mode 100644 index 00000000..a8c3bf82 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -0,0 +1,33 @@ +package pp.mdga; + +import java.util.ResourceBundle; + +/** + * Provides access to the resource bundle of the game. + * + * @see #BUNDLE + */ +public class Resources { + /** + * The resource bundle for the MDGA game. + */ + public static final ResourceBundle BUNDLE = ResourceBundle.getBundle("mdga"); //NON-NLS + + /** + * Gets a string for the given key from the resource bundle in {@linkplain #BUNDLE}. + * + * @param key the key for the desired string + * @return the string for the given key + * @throws NullPointerException if {@code key} is {@code null} + * @throws java.util.MissingResourceException if no object for the given key can be found + * @throws ClassCastException if the object found for the given key is not a string + */ + public static String lookup(String key) { + return BUNDLE.getString(key); + } + + /** + * Private constructor to prevent instantiation. + */ + private Resources() { /* do not instantiate */ } +} From c4304ae99a675c33eb728bcbf5a6d5bb6c534332 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 20:19:13 +0100 Subject: [PATCH 21/82] Updated 'Game' class. Updated the 'Game' class by removing the 'allReady' attribute in it. In Addtion, the 'areAllReady' method was added. --- .../src/main/java/pp/mdga/game/Game.java | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 4c27d8f2..9d7d83d5 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -48,9 +48,6 @@ public class Game { // The color of the active player. private Color activeColor; - // A flag indicating whether all players are ready. - private boolean allReady = false; - /** * This constructor creates a new Game object. */ @@ -114,6 +111,21 @@ public boolean isColorTaken(Color color) { return false; } + /** + * This method will be used to return the first unused color if possible. + * + * @return color as a Color enumeration. + */ + public Color getFirstUnusedColor() { + for (Color color : Color.values()) { + if (!isColorTaken(color)) { + return color; + } + } + + return null; + } + /** * This method will be used to return the player which has the given id parameter. * @@ -156,6 +168,22 @@ public int getNumberOfActivePlayers() { return activePlayers; } + /** + * This method will be used to check if all players are ready. + * If yes it will return true, otherwise false. + * + * @return true or false. + */ + public boolean areAllReady() { + for (Map.Entry entry : this.players.entrySet()) { + if (!entry.getValue().isReady()) { + return false; + } + } + + return true; + } + /** * This method will be used to return a piece based on the UUID. * @@ -351,22 +379,4 @@ public void setDie(Die die) { public void setHost(int host) { this.host = host; } - - /** - * This method returns the all ready state. - * - * @return the already state - */ - public Boolean allReady() { - return allReady; - } - - /** - * This method sets the all ready state. - * - * @param allReady the new all-ready state - */ - public void setAllReady(Boolean allReady) { - this.allReady = allReady; - } } From 7ddcdc3f483ff45f251a4865e619621a1c78c578 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 20:24:17 +0100 Subject: [PATCH 22/82] added the first error.messages and adjusted --- .../src/main/java/pp/mdga/client/MdgaApp.java | 2 +- .../pp/mdga/client/dialogState/LobbyState.java | 4 ++-- .../pp/mdga/server/automaton/LobbyState.java | 10 ---------- .../model/src/main/resources/mdga.properties | 18 ++++++++++++++++++ .../src/main/resources/mdga_de.properties | 14 ++++++++++++++ 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 41692841..a1df6b46 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -60,7 +60,7 @@ public class MdgaApp extends SimpleApplication { private ServerConnection networkConnection; - private MdgaApp() { + public MdgaApp() { networkConnection = new NetworkSupport(this); this.clientGameLogic = new ClientGameLogic(networkConnection); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index dcbe16f2..fb838357 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -63,9 +63,9 @@ public void selectUnready(){ @Override public void selectStart(){ - if(logic.isHost() && logic.getGame().allReady()){ + if(logic.isHost() && logic.getGame().areAllReady()){ logic.send(new StartGameMessage(false)); - } else if(logic.isHost() && !logic.getGame().allReady()) { + } else if(logic.isHost() && !logic.getGame().areAllReady()) { logic.send(new StartGameMessage(true)); } else { LOGGER.log(System.Logger.Level.ERROR, "You are not the host"); diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 167992c2..2f97e9c9 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -145,16 +145,6 @@ public void received(LobbyReadyMessage msg, int from) { return; } } - - this.logic.getGame().setAllReady(true); - if (this.logic.getGame().allReady()) { - this.logic.getServerSender().broadcast(new StartBriefingMessage(this.logic.getGame().getBoard())); - this.initializeGame(); - for (Map.Entry entry : logic.getGame().getBoard().getPlayerData().entrySet()) { - this.logic.getServerSender().broadcast(new PlayerDataMessage(entry.getValue(), entry.getKey())); - } - this.logic.getServerSender().broadcast(new ServerStartGameMessage()); - } } /** diff --git a/Projekte/mdga/model/src/main/resources/mdga.properties b/Projekte/mdga/model/src/main/resources/mdga.properties index e69de29b..0e2c35be 100644 --- a/Projekte/mdga/model/src/main/resources/mdga.properties +++ b/Projekte/mdga/model/src/main/resources/mdga.properties @@ -0,0 +1,18 @@ +incorrect.request.0= +incorrect.request.1= +incorrect.request.2= +incorrect.request.3= +incorrect.request.4= +incorrect.request.5= +incorrect.request.6= +incorrect.request.7= +incorrect.request.8= +incorrect.request.9= +incorrect.request.10= +incorrect.request.11= +incorrect.request.12= +incorrect.request.13= +incorrect.request.14= + + + diff --git a/Projekte/mdga/model/src/main/resources/mdga_de.properties b/Projekte/mdga/model/src/main/resources/mdga_de.properties index e69de29b..cdad81cc 100644 --- a/Projekte/mdga/model/src/main/resources/mdga_de.properties +++ b/Projekte/mdga/model/src/main/resources/mdga_de.properties @@ -0,0 +1,14 @@ +incorrect.request.1=Die ausgewählte TSK ist bereits belegt. +incorrect.request.2=Es gibt keine freie TSK mehr, welche ausgewählt werden kann. +incorrect.request.3= +incorrect.request.4= +incorrect.request.5= +incorrect.request.6= +incorrect.request.7= +incorrect.request.8= +incorrect.request.9= +incorrect.request.10= +incorrect.request.11= +incorrect.request.12= +incorrect.request.13= +incorrect.request.14= From d062b9dabc9e3c8f16637e87b2c2b4cfe3f40079 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 20:32:46 +0100 Subject: [PATCH 23/82] Add forceStartGameButton to host in lobby --- .../src/main/java/pp/mdga/client/view/LobbyView.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java index e96cbbb7..31c6a3db 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java @@ -18,6 +18,7 @@ import pp.mdga.client.button.LobbyButton; import pp.mdga.client.button.SettingsButton; import pp.mdga.game.Color; +import pp.mdga.message.client.StartGameMessage; import pp.mdga.notification.GameNotification; public class LobbyView extends MdgaView { @@ -25,6 +26,7 @@ public class LobbyView extends MdgaView { private ButtonLeft leaveButton; private ButtonRight readyButton; + private ButtonRight startButton; private LobbyButton cyberButton; private LobbyButton airforceButton; @@ -42,6 +44,7 @@ public LobbyView(MdgaApp app) { leaveButton = new ButtonLeft(app, guiNode, this::leaveLobby, "Verlassen", 1); readyButton = new ButtonRight(app, guiNode, this::ready, "Bereit", 1); + startButton = new ButtonRight(app, guiNode, () -> app.getGameLogic().selectStart(), "Starten", 7); cyberButton = new LobbyButton(app, guiNode, rootNode, () -> toggleTsk(Color.CYBER), Color.CYBER); airforceButton = new LobbyButton(app, guiNode, rootNode, () -> toggleTsk(Color.AIRFORCE), Color.AIRFORCE); @@ -61,6 +64,10 @@ public void onEnter() { leaveButton.show(); readyButton.show(); + if(app.getGameLogic().isHost()) { + startButton.show(); + } + cyberButton.show(); airforceButton.show(); armyButton.show(); @@ -95,6 +102,7 @@ public void onEnter() { public void onLeave() { leaveButton.hide(); readyButton.hide(); + startButton.hide(); airforceButton.hide(); armyButton.hide(); From 73859d8c81e41109e292bd6c249bde5293a6ca84 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 20:34:52 +0100 Subject: [PATCH 24/82] added methods for getting Boolean, String, Double and int --- .../model/src/main/java/pp/mdga/Resources.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java index a8c3bf82..10ae5a44 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -22,10 +22,22 @@ public class Resources { * @throws java.util.MissingResourceException if no object for the given key can be found * @throws ClassCastException if the object found for the given key is not a string */ - public static String lookup(String key) { + public static String stringLookup(String key) { return BUNDLE.getString(key); } + public static int intLookup(String key) { + return Integer.parseInt(BUNDLE.getString(key)); + } + + public static boolean boolLookup(String key) { + return Boolean.parseBoolean(BUNDLE.getString(key)); + } + + public static double doubleLookup(String key) { + return Double.parseDouble(BUNDLE.getString(key)); + } + /** * Private constructor to prevent instantiation. */ From c48f924ead2cc27f8288f6dab18c94ee00a9df7c Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 20:43:47 +0100 Subject: [PATCH 25/82] Updated 'Resources' class. Updated the 'Resources' class by adding the 'MAX_PLAYERS' constant to it. --- Projekte/mdga/model/src/main/java/pp/mdga/Resources.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java index 10ae5a44..a7142c45 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -8,6 +8,11 @@ * @see #BUNDLE */ public class Resources { + /** + * Create Resources constants. + */ + public static final int MAX_PLAYERS = 4; + /** * The resource bundle for the MDGA game. */ From c1fa67926180422f2f6699676a8dc83300730ff6 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 20:45:07 +0100 Subject: [PATCH 26/82] Updated 'Resources' class. Updated the 'Resources' class by adding the 'MAX_PIECES' constant to it. --- Projekte/mdga/model/src/main/java/pp/mdga/Resources.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java index a7142c45..f721043d 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -12,6 +12,7 @@ public class Resources { * Create Resources constants. */ public static final int MAX_PLAYERS = 4; + public static final int MAX_PIECES = 4; /** * The resource bundle for the MDGA game. From 3eef4b2a02df82b092b441bf4171001e59fc5d53 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 20:56:59 +0100 Subject: [PATCH 27/82] Updated 'PlayerData' class. Updated the 'PlayerData' class by replacing the magic constants with the 'Resources' class. In Addition, some JavaDoc texts were updated. --- .../main/java/pp/mdga/game/PlayerData.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java index cb2a788b..ae3387e4 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java @@ -1,6 +1,7 @@ package pp.mdga.game; import com.jme3.network.serializing.Serializable; +import pp.mdga.Resources; /** * This class is used to represent PlayerData related to the board @@ -33,10 +34,10 @@ public class PlayerData { * @param color the color of the player */ public PlayerData(Color color) { - homeNodes = new HomeNode[4]; - pieces = new Piece[4]; - waitingArea = new Piece[4]; - for (int i = 0; i < 4; i++) { + homeNodes = new HomeNode[Resources.MAX_PIECES]; + pieces = new Piece[Resources.MAX_PIECES]; + waitingArea = new Piece[Resources.MAX_PIECES]; + for (int i = 0; i < Resources.MAX_PIECES; i++) { homeNodes[i] = new HomeNode(); pieces[i] = new Piece(color, PieceState.WAITING, i); waitingArea[i] = pieces[i]; @@ -47,9 +48,9 @@ public PlayerData(Color color) { * Constructor. */ private PlayerData() { - homeNodes = new HomeNode[4]; - waitingArea = new Piece[4]; - pieces = new Piece[4]; + homeNodes = new HomeNode[Resources.MAX_PIECES]; + waitingArea = new Piece[Resources.MAX_PIECES]; + pieces = new Piece[Resources.MAX_PIECES]; } /** @@ -113,7 +114,7 @@ public Piece[] getPieces() { * @param piece the piece to be added to the waiting area */ public void addWaitingPiece(Piece piece) { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < Resources.MAX_PIECES; i++) { if (waitingArea[i] == null) { waitingArea[i] = piece; return; @@ -127,7 +128,7 @@ public void addWaitingPiece(Piece piece) { * @return the piece that was removed from the waiting area */ public Piece removePieceFromWaitingArea() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < Resources.MAX_PIECES; i++) { if (waitingArea[i] != null) { Piece piece = waitingArea[i]; waitingArea[i] = null; @@ -148,26 +149,30 @@ public void setPieceInHome(int index, Piece piece) { } /** - * This method returns the homeNodes + * This method will be used to return if the given piece parameter is inside the homNodes attribute of PlayerData + * class. + * If yes it will return true, otherwise false. * - * @return the homeNodes + * @return true or false. */ public boolean homeIncludes(Piece piece) { - for (int i = 0; i < 4; i++) { - if (homeNodes[i].getOccupant() == piece) { + for (Node node : this.homeNodes) { + if (node.getOccupant() == piece) { return true; } } + return false; } /** - * This method returns the homeNodes + * This method will be used to return the index of the given piece parameter in the homeNodes attribute of + * PlayerData class. * - * @return the homeNodes + * @return index as an Integer. */ public int getIndexInHome(Piece piece) { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < Resources.MAX_PIECES; i++) { if (homeNodes[i].getOccupant() == piece) { return i; } @@ -176,9 +181,10 @@ public int getIndexInHome(Piece piece) { } /** - * This method returns the homeNodes + * This method will be usd to check if the waitingArea attribute of PlayerData class is empty. + * If yes it will return false, otherwise true. * - * @return the homeNodes + * @return true or false. */ public boolean hasPieceInWaitingArea() { for (Piece piece : waitingArea) { From ebb9f839c7bc76a245bb83605d890828e3ad8fc9 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 20:58:00 +0100 Subject: [PATCH 28/82] added JavaDocs in Resources.java --- .../src/main/java/pp/mdga/Resources.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java index f721043d..13a60c48 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -32,14 +32,41 @@ public static String stringLookup(String key) { return BUNDLE.getString(key); } + /** + * Gets a int for the given key from the resource bundle in {@linkplain #BUNDLE}. + * + * @param key the key for the desired string + * @return the string for the given key + * @throws NullPointerException if {@code key} is {@code null} + * @throws java.util.MissingResourceException if no object for the given key can be found + * @throws ClassCastException if the object found for the given key is not a string + */ public static int intLookup(String key) { return Integer.parseInt(BUNDLE.getString(key)); } + /** + * Gets a boolean for the given key from the resource bundle in {@linkplain #BUNDLE}. + * + * @param key the key for the desired string + * @return the string for the given key + * @throws NullPointerException if {@code key} is {@code null} + * @throws java.util.MissingResourceException if no object for the given key can be found + * @throws ClassCastException if the object found for the given key is not a string + */ public static boolean boolLookup(String key) { return Boolean.parseBoolean(BUNDLE.getString(key)); } + /** + * Gets a double for the given key from the resource bundle in {@linkplain #BUNDLE}. + * + * @param key the key for the desired string + * @return the string for the given key + * @throws NullPointerException if {@code key} is {@code null} + * @throws java.util.MissingResourceException if no object for the given key can be found + * @throws ClassCastException if the object found for the given key is not a string + */ public static double doubleLookup(String key) { return Double.parseDouble(BUNDLE.getString(key)); } From 1cf14f65bb840da66a83e53742774b54bec6e8fe Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 21:04:36 +0100 Subject: [PATCH 29/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by updating the 'connectionAdded' method in it. In Addition, the JavaDoc text for this method was addded. --- .../pp/mdga/client/server/MdgaServer.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index 20264ef9..a23348ee 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -2,6 +2,7 @@ import com.jme3.network.*; import com.jme3.network.serializing.Serializer; +import pp.mdga.Resources; import pp.mdga.game.*; import pp.mdga.message.client.*; import pp.mdga.message.server.*; @@ -195,12 +196,29 @@ private void messageReceived(HostedConnection source, ClientMessage message) { pendingMessages.add(new ReceivedMessage(message, source.getId())); } + /** + * This method will be used to handle all connections which are connected to the server. + * It will check if the maximum number of connected clients are already reached. If yes it will send a + * LobbyDenyMessage to the given hostedConnection parameter and close it, otherwise it will send a + * LobbyAcceptMessage to the given hostedConnection parameter. In Addition, if the number of connected clients is + * equal to 1 it will set the host of the game to the id of the given hostedConnection parameter. + * + * @param server as the server which is contains all connections as a Server object. + * @param hostedConnection as the connection which is added to the server as a HostedConnection object. + */ @Override public void connectionAdded(Server server, HostedConnection hostedConnection) { System.out.println("new connection " + hostedConnection); //NON-NLS LOGGER.log(Level.DEBUG, "new connection {0}", hostedConnection); //NON-NLS - if (this.myServer.getConnections().size() == 1) { - this.logic.getGame().setHost(hostedConnection.getId()); + + if (this.myServer.getConnections().size() > Resources.MAX_PLAYERS) { + this.logic.getServerSender().send(hostedConnection.getId(), new LobbyDenyMessage()); + hostedConnection.close(""); + } else { + if (this.myServer.getConnections().size() == 1) { + this.logic.getGame().setHost(hostedConnection.getId()); + } + this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage()); } } From 7fcee3cac0d66d8095e60ca4c9c96580af2b2fed Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 21:11:59 +0100 Subject: [PATCH 30/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by register the 'IncorrectRequestMessage' class to the serializer. --- .../client/src/main/java/pp/mdga/client/server/MdgaServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index a23348ee..bbcfe2c4 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -135,6 +135,7 @@ private void initializeSerializables() { Serializer.registerClass(UpdateReadyMessage.class); Serializer.registerClass(UpdateTSKMessage.class); Serializer.registerClass(WaitPieceMessage.class); + Serializer.registerClass(IncorrectRequestMessage.class); Serializer.registerClass(Player.class); Serializer.registerClass(Statistic.class); Serializer.registerClass(Board.class); From 8d39d61c71d9cf031cead288eb7d6e1c32cf265f Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 21:12:43 +0100 Subject: [PATCH 31/82] Add infoNotification --- .../mdga/notification/InfoNotification.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Projekte/mdga/model/src/main/java/pp/mdga/notification/InfoNotification.java diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/notification/InfoNotification.java b/Projekte/mdga/model/src/main/java/pp/mdga/notification/InfoNotification.java new file mode 100644 index 00000000..4ccb87d6 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/notification/InfoNotification.java @@ -0,0 +1,33 @@ +package pp.mdga.notification; + +import pp.mdga.game.BonusCard; +/** + * Notification that is to give information to the player. + */ +public class InfoNotification extends Notification{ + + private final String message; + + private boolean isError = false; + + /** + * Constructor. + */ + public InfoNotification(String info) { + this.message = info; + } + + /** + * Constructor. + */ + public InfoNotification(String info, boolean isError) { + this.message = info; + this.isError = isError; + } + + public String getMessage() { + return message; + } + + public boolean isError() { return isError; } +} From e87eb569c2d2b35c1df74375479e6a9a725805a0 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 21:16:50 +0100 Subject: [PATCH 32/82] Add showInfo to MdgaView --- .../mdga/client/NotificationSynchronizer.java | 5 +++ .../java/pp/mdga/client/view/MainView.java | 4 +++ .../java/pp/mdga/client/view/MdgaView.java | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index f23eda0e..1cd3c927 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -27,6 +27,11 @@ public void addTestNotification(Notification n) { public void update() { Notification n = app.getGameLogic().getNotification(); + if(n instanceof InfoNotification infoNotification) { + app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError()); + return; + } + if(n != null) { switch (app.getState()) { case MAIN: diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java index 6ebf9e33..81e31d8e 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java @@ -1,8 +1,12 @@ package pp.mdga.client.view; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; import com.jme3.scene.Geometry; import pp.mdga.client.MdgaApp; import pp.mdga.client.acoustic.MdgaSound; +import pp.mdga.client.button.AbstractButton; +import pp.mdga.client.button.LabelButton; import pp.mdga.client.dialog.HostDialog; import pp.mdga.client.dialog.JoinDialog; import pp.mdga.client.dialog.StartDialog; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java index 9dd4f1dc..ebb105b8 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java @@ -2,9 +2,12 @@ import com.jme3.asset.TextureKey; import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Quad; +import com.jme3.system.NanoTimer; import com.jme3.texture.Texture; import pp.mdga.client.MdgaApp; import pp.mdga.client.acoustic.MdgaSound; @@ -30,6 +33,9 @@ public enum Overlay { private VideoSettingsDialog videoSettingsDialog; private AudioSettingsDialog audioSettingsDialog; + protected LabelButton infoLabel = null; + protected NanoTimer infoTimer = new NanoTimer(); + private int settingsDepth = 0; public MdgaView(MdgaApp app) { @@ -80,6 +86,11 @@ public void update(float tpf) { videoSettingsDialog.update(); audioSettingsDialog.update(); + if (null != infoLabel && infoTimer.getTimeInSeconds() > 7) { + infoLabel.hide(); + infoLabel = null; + } + onUpdate(tpf); } @@ -193,4 +204,24 @@ public void pressForward() { ceremonyView.forward(); } } + + public void showInfo(String error, boolean isError) { + infoTimer.reset(); + + if(null != infoLabel) { + infoLabel.hide(); + } + + infoLabel = new LabelButton(app, guiNode, error, new Vector2f(5.5f, 2), new Vector2f(0.5f, AbstractButton.VERTICAL - 0.5f), false); + + ColorRGBA color; + + if(isError) { + color = ColorRGBA.Red.clone(); + } else { + color = ColorRGBA.Green.clone(); + } + + infoLabel.setColor(ColorRGBA.Black, color); + } } From 72321eab9a397cadae4748c41cb9dffa7fa225a0 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 21:22:49 +0100 Subject: [PATCH 33/82] Updated 'LobbyState' class. Updated the 'LobbyState' class by updating all received methods in it. --- .../pp/mdga/server/automaton/LobbyState.java | 53 ++++++------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 2f97e9c9..7d49a751 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -80,18 +80,13 @@ public void received(JoinedLobbyMessage msg, int from) { */ @Override public void received(SelectTSKMessage msg, int from) { - for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { - if (entry.getValue().getColor() == msg.getColor()) { - return; - } + if (msg.getColor() != Color.NONE && !this.logic.getGame().isColorTaken(msg.getColor())) { + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, Color.NONE, false)); + this.logic.getGame().getPlayerById(from).setColor(msg.getColor()); + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, msg.getColor(), true)); + } else { + this.logic.getServerSender().send(from, new IncorrectRequestMessage(0)); } - - if (this.logic.getGame().getPlayerById(from).getColor() != Color.NONE) { - this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, this.logic.getGame().getPlayerById(from).getColor(), false)); - } - - this.logic.getGame().getPlayerById(from).setColor(msg.getColor()); - this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, msg.getColor(), true)); } /** @@ -116,34 +111,21 @@ public void received(DeselectTSKMessage msg, int from) { */ @Override public void received(LobbyReadyMessage msg, int from) { - //assign a free color if (this.logic.getGame().getPlayerById(from).getColor() == Color.NONE) { - ArrayList colors = new ArrayList<>(); - colors.add(Color.ARMY); - colors.add(Color.AIRFORCE); - colors.add(Color.NAVY); - colors.add(Color.CYBER); + Color color = this.logic.getGame().getFirstUnusedColor(); - for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { - if (colors.contains(entry.getValue().getColor())) { - colors.remove(entry.getValue().getColor()); - } + if (color != null) { + this.logic.getGame().getPlayerById(from).setColor(color); + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, color, true)); + } else { + this.logic.getServerSender().send(from, new IncorrectRequestMessage(1)); } - - if (colors.size() < 1) { - throw new RuntimeException("can not assign a color"); - } - - this.logic.getGame().getPlayerById(from).setColor(colors.get(0)); - this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, colors.get(0), true)); } - this.logic.getGame().getPlayerById(from).setReady(true); - this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, true)); - for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { - if (!entry.getValue().isReady()) { - return; - } + if (this.logic.getGame().areAllReady()) { + this.initializeGame(); + this.logic.getServerSender().broadcast(new ServerStartGameMessage()); + this.logic.setCurrentState(this.logic.getGameState()); } } @@ -157,7 +139,6 @@ public void received(LobbyReadyMessage msg, int from) { @Override public void received(LobbyNotReadyMessage msg, int from) { this.logic.getGame().getPlayerById(from).setReady(false); - this.logic.getGame().setAllReady(false); this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, false)); } @@ -187,7 +168,7 @@ public void received(LeaveGameMessage msg, int from) { */ @Override public void received(StartGameMessage msg, int from) { - if (msg.isForceStartGame() || this.logic.getGame().allReady()) { + if (msg.isForceStartGame() || this.logic.getGame().areAllReady()) { this.initializeGame(); this.logic.getServerSender().broadcast(new ServerStartGameMessage()); this.logic.setCurrentState(this.logic.getGameState()); From 468e4005dc0e315a163cdc5f623c8f9c09856044 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 21:23:53 +0100 Subject: [PATCH 34/82] Updated 'LobbyState' class. Updated the 'LobbyState' class by sending a broadcast update the new ready state of the client. --- .../model/src/main/java/pp/mdga/server/automaton/LobbyState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 7d49a751..9093dd90 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -122,6 +122,7 @@ public void received(LobbyReadyMessage msg, int from) { } } this.logic.getGame().getPlayerById(from).setReady(true); + this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, true)); if (this.logic.getGame().areAllReady()) { this.initializeGame(); this.logic.getServerSender().broadcast(new ServerStartGameMessage()); From 5ae65921bf97ce290562676b8f92e68ee024a4d5 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 21:42:12 +0100 Subject: [PATCH 35/82] Add more random names --- .../pp/mdga/client/dialog/StartDialog.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java index 5f70f4d6..412daa6c 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java @@ -177,6 +177,85 @@ public String getName() { "CarryPro", "ProBaiter", "GameWarden", + "KartoffelKönig", + "SaufenderWolf", + "WurstGriller", + "Flitzekacke", + "BratwurstBub", + "Hoppeldoppels", + "BananenMensch", + "KlopapierGuru", + "SchnitzelKing", + "NerdNomade", + "Dönertänzer", + "GlitzerGurke", + "SchinkenShrek", + "KäseKalle", + "SchokoSchnecke", + "KeksKämpfer", + "QuarkPiraten", + "Müslimonster", + "KnuddelNase", + "FantaFighter", + "SchnapsSaurier", + "Wackelpudding", + "ZitronenZock", + "FettWurst", + "PlüschPanda", + "Zuckerschnur", + "FluffiKopf", + "DonutDöner", + "VollpfostenX", + "Schraubenschlüssel", + "Witzepumper", + "ToastTraum", + "FroschFighter", + "KrümelTiger", + "RegenWolke", + "PuddingPower", + "KoffeinKrieger", + "SpeckSchlumpf", + "SuperSuppe", + "BierBärchen", + "FischBär", + "Flauschi", + "Schokomonster", + "ChaosKäse", + "FlitzLappen", + "WurstWombat", + "KrümelMensch", + "PuddingBär", + "ZickZack", + "Schwabel", + "Fluffi", + "RülpsFrosch", + "PommesPapa", + "QuarkBär", + "KnusperKönig", + "ToastBrot", + "Ploppster", + "Schleimschwein", + "Äpfelchen", + "Knallbonbon", + "KaffeeKopf", + "WackelWurst", + "RennKeks", + "BröselBub", + "ZockerBrot", + "BierWurm", + "StinkFlummi", + "SchlumpfKing", + "PurzelBär", + "FlinkFluff", + "PloppPudel", + "Schnorchel", + "FliegenKopf", + "PixelPommes", + "SchwipsWürst", + "WutzBär", + "KnuddelKeks", + "FantaFlumm", + "ZockerKäse" }; Random random = new Random(); From 7d54a906ddb257ffb5a1fa2cbb11c10a7c3523d0 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 21:48:23 +0100 Subject: [PATCH 36/82] Add some more names --- .../pp/mdga/client/dialog/StartDialog.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java index 412daa6c..44a8f746 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java @@ -256,6 +256,30 @@ public String getName() { "KnuddelKeks", "FantaFlumm", "ZockerKäse" + "LachHäufchen", + "GurkenGuru", + "PonySchnitzel", + "NudelNinja", + "VulkanKeks", + "WasserToast", + "MenschSalat", + "KampfKohlenhydrate", + "SockenZirkus", + "SchwimmBärchen", + "TanzenderDachgepäckträger", + "PizzamarktMensch", + "ZahnarztZocker", + "RollerCoasterTester", + "WaschmaschinenPilot", + "WitzigeZwiebel", + "Pillenschlucker", + "ZwiebelReiter", + "HüpfenderKaktus", + "KochenderAsteroid", + "ChaosKarotte", + "WolkenFurz", + "SchnitzelPartikel", + "WackelBiene", }; Random random = new Random(); From e94ed1e019f57aecf52463db243e97ef1dcd4b9a Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Mon, 2 Dec 2024 21:51:09 +0100 Subject: [PATCH 37/82] Fix syntax error --- .../client/src/main/java/pp/mdga/client/dialog/StartDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java index 44a8f746..870e8ae7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/StartDialog.java @@ -255,7 +255,7 @@ public String getName() { "WutzBär", "KnuddelKeks", "FantaFlumm", - "ZockerKäse" + "ZockerKäse", "LachHäufchen", "GurkenGuru", "PonySchnitzel", From 5910fcc70100afda5ba2d3204a87c496fe2e8091 Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 21:52:07 +0100 Subject: [PATCH 38/82] added the client logic to receive the LobbyAccept and LobbyDeny message --- .../java/pp/mdga/client/view/MainView.java | 11 +--- .../main/java/pp/mdga/client/ClientState.java | 62 +++++++++---------- .../java/pp/mdga/client/DialogsState.java | 20 ++++++ .../dialogState/NetworkDialogState.java | 38 +++++++++++- .../client/dialogState/StartDialogState.java | 31 +++++++++- .../message/server/LobbyAcceptMessage.java | 2 +- .../mdga/message/server/LobbyDenyMessage.java | 2 +- .../model/src/main/resources/mdga.properties | 20 ++---- .../src/main/resources/mdga_de.properties | 14 +---- 9 files changed, 128 insertions(+), 72 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java index 81e31d8e..df534142 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java @@ -112,11 +112,8 @@ private void tryHost() { } catch (InterruptedException ignored) { } hostDialog.connectServerAsClient(); - try { - Thread.sleep(1000); - } catch (InterruptedException ignored) { - } app.getModelSynchronize().setHost(port); + app.getGameLogic().selectHost(""); //app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT); return; } @@ -144,11 +141,7 @@ private void tryJoin() { app.getModelSynchronize().setName(startDialog.getName()); joinDialog.setHostname(ip); joinDialog.connectToServer(); - try { - Thread.sleep(1000); - } catch (InterruptedException ignored) { - } - app.getModelSynchronize().setJoin(ip, port); + app.getGameLogic().selectJoin(""); return; } } catch (IllegalArgumentException e) { diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java index 12559a93..998ea5a7 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java @@ -37,155 +37,155 @@ public String toString(){ @Override public void received(ActivePlayerMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(AnyPieceMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(BriefingMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(CeremonyMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(DieMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(DiceAgainMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(DiceNowMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(EndOfTurnMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(LobbyAcceptMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(LobbyDenyMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(LobbyPlayerJoinedMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(LobbyPlayerLeaveMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(MoveMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(NoTurnMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(PauseGameMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(PlayCardMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(PossibleCardMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(PossiblePieceMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(RankingResponseMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(RankingRollAgainMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(ReconnectBriefingMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(ResumeGameMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(ServerStartGameMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override - public void received(ShutdownMessage msg) {LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);} + public void received(ShutdownMessage msg) {LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString());} @Override public void received(StartPieceMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(UpdateReadyMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(UpdateTSKMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(SpectatorMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(SelectPieceMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(WaitPieceMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } @Override public void received(IncorrectRequestMessage msg) { - LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg.toString()); } public void selectPiece(Piece piece) { diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index c6f28994..ef2dccdf 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -254,6 +254,26 @@ public void received(ServerStartGameMessage msg){ currentState.received(msg); } + /** + * This method is used to call the received method of the current state + * + * @param msg the LobbyAccept message received + */ + @Override + public void received(LobbyAcceptMessage msg){ + currentState.received(msg); + } + + /** + * This method is used to call the received method of the current state + * + * @param msg the LobbyDeny message received + */ + @Override + public void received(LobbyDenyMessage msg){ + currentState.received(msg); + } + /** * This method is used to get the current state */ diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java index 98234319..a5ef0cba 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java @@ -1,34 +1,70 @@ package pp.mdga.client.dialogState; +import pp.mdga.Resources; import pp.mdga.client.ClientGameLogic; import pp.mdga.client.ClientState; import pp.mdga.client.DialogsState; +import pp.mdga.message.server.LobbyAcceptMessage; +import pp.mdga.message.server.LobbyDenyMessage; +import pp.mdga.notification.InfoNotification; import pp.mdga.notification.LobbyDialogNotification; public class NetworkDialogState extends DialogStates { private final DialogsState parent; + /** + * Constructor for the NetworkDialogState + * @param parent the parent state + * @param logic the logic + */ public NetworkDialogState(ClientState parent, ClientGameLogic logic) { super(parent, logic); this.parent = (DialogsState) parent; } + /** + * Enter the state + */ @Override public void enter() { + LOGGER.log(System.Logger.Level.INFO, "Entered {0}", this); } + /** + * Exit the state + */ @Override public void exit() { } + /** + * Select the leave option + */ @Override public void selectLeave() { parent.setState(parent.getStartDialog()); } - public void selectJoin(String IP) { + /** + * This method is called when the server accepts the client into the lobby + * + * @param msg the LobbyAcceptMessage + */ + @Override + public void received(LobbyAcceptMessage msg) { parent.setState(parent.getLobby()); logic.addNotification(new LobbyDialogNotification()); } + + /** + * This method is called when the server denies the client into the lobby + * + * @param msg the LobbyDenyMessage + */ + @Override + public void received(LobbyDenyMessage msg) { + logic.addNotification(new InfoNotification(Resources.stringLookup("lobby.deny.join"))); + parent.setState(parent.getStartDialog()); + } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java index 47b9944a..618387d2 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java @@ -8,21 +8,35 @@ public class StartDialogState extends DialogStates { private final DialogsState parent; + /** + * Constructor for the StartDialogState + * @param parent the parent state + * @param logic the logic + */ public StartDialogState(ClientState parent, ClientGameLogic logic) { super(parent, logic); this.parent = (DialogsState) parent; } + /** + * Enter the state + */ @Override public void enter() { - } + /** + * Exit the state + */ @Override public void exit() { - } + /** + * Select the join option + * + * @param name the name + */ @Override public void selectJoin(String name) { parent.setOwnPlayerName(name); @@ -30,6 +44,11 @@ public void selectJoin(String name) { logic.setHost(false); } + /** + * Select the host option + * + * @param name the name + */ @Override public void selectHost(String name) { parent.setOwnPlayerName(name); @@ -37,12 +56,20 @@ public void selectHost(String name) { logic.setHost(true); } + /** + * Set the name + * + * @param name the name + */ @Override public void setName(String name) { parent.setState(parent.getNetworkDialog()); parent.setOwnPlayerName(name); } + /** + * Select the leave option + */ @Override public void selectLeave() { parent.exit(); diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java index f3e16386..90b55126 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java @@ -31,7 +31,7 @@ public void accept(ServerInterpreter interpreter) { */ @Override public String toString() { - return ""; + return "Lobby Accept"; } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyDenyMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyDenyMessage.java index e49057b7..ea5299ee 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyDenyMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyDenyMessage.java @@ -31,7 +31,7 @@ public void accept(ServerInterpreter interpreter) { */ @Override public String toString() { - return ""; + return "LobbyDeny"; } /** diff --git a/Projekte/mdga/model/src/main/resources/mdga.properties b/Projekte/mdga/model/src/main/resources/mdga.properties index 0e2c35be..fc9ab8ae 100644 --- a/Projekte/mdga/model/src/main/resources/mdga.properties +++ b/Projekte/mdga/model/src/main/resources/mdga.properties @@ -1,18 +1,8 @@ -incorrect.request.0= -incorrect.request.1= -incorrect.request.2= -incorrect.request.3= -incorrect.request.4= -incorrect.request.5= -incorrect.request.6= -incorrect.request.7= -incorrect.request.8= -incorrect.request.9= -incorrect.request.10= -incorrect.request.11= -incorrect.request.12= -incorrect.request.13= -incorrect.request.14= +lobby.deny.join=The lobby is already full. + +incorrect.request.0=The selected TSK is already occupied. +incorrect.request.1=No TSK is available for selection. + diff --git a/Projekte/mdga/model/src/main/resources/mdga_de.properties b/Projekte/mdga/model/src/main/resources/mdga_de.properties index cdad81cc..dc3490b0 100644 --- a/Projekte/mdga/model/src/main/resources/mdga_de.properties +++ b/Projekte/mdga/model/src/main/resources/mdga_de.properties @@ -1,14 +1,4 @@ +lobby.deny.join=Die Lobby ist bereits voll. + incorrect.request.1=Die ausgewählte TSK ist bereits belegt. incorrect.request.2=Es gibt keine freie TSK mehr, welche ausgewählt werden kann. -incorrect.request.3= -incorrect.request.4= -incorrect.request.5= -incorrect.request.6= -incorrect.request.7= -incorrect.request.8= -incorrect.request.9= -incorrect.request.10= -incorrect.request.11= -incorrect.request.12= -incorrect.request.13= -incorrect.request.14= From 27f8af70f57323411d3d3595bfcfaa6053eb912a Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 22:34:45 +0100 Subject: [PATCH 39/82] Updated 'Game' class. Updated the 'Game' class by setting the default value of 'host' attribute. In Addition, the 'isHost' method was added. --- .../mdga/model/src/main/java/pp/mdga/game/Game.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 9d7d83d5..515aa1da 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -43,7 +43,7 @@ public class Game { private Die die; // The host of this game - private int host; + private int host = -1; // The color of the active player. private Color activeColor; @@ -200,6 +200,15 @@ public Piece getPieceThroughUUID(UUID pieceId) { return null; } + /** + * This method will be used to check if this client is the host for the game. + * + * @return true or false. + */ + public boolean isHost() { + return this.host != -1; + } + /** * This method returns the dice modifier. * From 492f7422f5d591c1adc9fa0bb1e42919c4a19eeb Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 22:36:48 +0100 Subject: [PATCH 40/82] Updated 'LobbyAcceptMessage' class. Updated the 'LobbyAcceptMessage' class by adding the 'host' attribute and its getter method to it. --- .../message/server/LobbyAcceptMessage.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java index 90b55126..fbaf7c56 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/LobbyAcceptMessage.java @@ -7,13 +7,39 @@ */ @Serializable public class LobbyAcceptMessage extends ServerMessage { + /** + * Create LobbyAcceptMessage attributes. + */ + private final int host; + /** * Constructs a new LobbyAccept instance. */ public LobbyAcceptMessage() { super(); + this.host = -1; } + /** + * Constructor. + * + * @param host as the id of the host as an Integer. + */ + public LobbyAcceptMessage(int host) { + super(); + this.host = host; + } + + /** + * This method will be used return host attribute of LobbyAcceptMessage class. + * + * @return host as an Integer. + */ + public int getHost() { + return this.host; + } + + /** * Accepts a visitor to process this message. * From 1eb24b7a668f53699cf50278509de55a0fc12e70 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 22:37:46 +0100 Subject: [PATCH 41/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by updating the logic inside the 'connectionAdded' method in it. --- .../client/src/main/java/pp/mdga/client/server/MdgaServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index bbcfe2c4..f3301618 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -216,7 +216,7 @@ public void connectionAdded(Server server, HostedConnection hostedConnection) { this.logic.getServerSender().send(hostedConnection.getId(), new LobbyDenyMessage()); hostedConnection.close(""); } else { - if (this.myServer.getConnections().size() == 1) { + if (hostedConnection.getAddress().contains("127.0.0.1") && this.logic.getGame().getHost() == -1) { this.logic.getGame().setHost(hostedConnection.getId()); } this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage()); From e163b87cc471b059060172aa5c5d3aef299203e8 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 22:41:17 +0100 Subject: [PATCH 42/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by updating the logic inside the 'connectionAdded' method in it. --- .../src/main/java/pp/mdga/client/server/MdgaServer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index f3301618..97295da2 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -218,8 +218,10 @@ public void connectionAdded(Server server, HostedConnection hostedConnection) { } else { if (hostedConnection.getAddress().contains("127.0.0.1") && this.logic.getGame().getHost() == -1) { this.logic.getGame().setHost(hostedConnection.getId()); + this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage(hostedConnection.getId())); + } else { + this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage()); } - this.logic.getServerSender().send(hostedConnection.getId(), new LobbyAcceptMessage()); } } From aa651ec62f72dbb9722592bd49e9ed1bb75ffc78 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Mon, 2 Dec 2024 22:43:03 +0100 Subject: [PATCH 43/82] added trees --- .../src/main/java/pp/mdga/client/Asset.java | 4 +- .../java/pp/mdga/client/board/MapLoader.java | 4 +- .../client/src/main/resources/Maps/map.mdga | 143 +++++++++++++++++- .../resources/Models/treeBig/treeBig_diff.png | Bin 0 -> 95468 bytes .../Models/treeSmall/treeSmall_diff.png | Bin 0 -> 60253 bytes 5 files changed, 145 insertions(+), 6 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java index 1f3da542..60dfebcb 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java @@ -30,8 +30,8 @@ public enum Asset { tank, world(1.2f), shieldRing("Models/shieldRing/shieldRing.j3o", null), - treeSmall, - treeBig, + treeSmall(1.2f), + treeBig(1.2f), turboCard, turboSymbol("Models/turboCard/turboSymbol.j3o", "Models/turboCard/turboCard_diff.j3o"), swapCard, diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java index 9d283f8f..76b286da 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java @@ -107,8 +107,8 @@ private static Asset getLoadedAsset(String assetName) { case "radar" -> Asset.radar; case "ship" -> Asset.ship; case "tank" -> Asset.tank; - case "tree_small" -> Asset.treeSmall; - case "tree_big" -> Asset.treeBig; + case "treeSmall" -> Asset.treeSmall; + case "treeBig" -> Asset.treeBig; default -> throw new IllegalStateException("Unexpected value: " + assetName); }; } diff --git a/Projekte/mdga/client/src/main/resources/Maps/map.mdga b/Projekte/mdga/client/src/main/resources/Maps/map.mdga index 5e906521..2989a3af 100644 --- a/Projekte/mdga/client/src/main/resources/Maps/map.mdga +++ b/Projekte/mdga/client/src/main/resources/Maps/map.mdga @@ -1,7 +1,5 @@ world 0,0 90 -#tree_small 1,1 0 -#tree_big 0,0 0 #Marine Pos marine 4,-5 270 @@ -131,3 +129,144 @@ node_home_blue 4,0 0 node_home_blue 3,0 0 node_home_blue 2,0 0 node_home_blue 1,0 0 + +# Randomly Distributed Trees within Radius 12 to 40 + +treeSmall 10,15 180 +treeBig -15,12 45 +treeSmall -8,-22 270 +treeBig 22,8 90 +treeSmall -18,-10 135 +treeBig 9,24 300 +treeSmall 17,-9 60 +treeBig -20,5 330 +treeSmall -14,18 200 +treeBig 25,-7 120 +treeBig -12,-18 150 +treeSmall 19,-16 45 +treeBig 7,10 90 +treeBig -19,-9 270 +treeSmall 21,4 110 +treeBig -11,17 300 +treeSmall 3,-21 360 +treeSmall -23,14 100 +treeBig 4,26 330 +treeSmall 12,13 270 +treeBig -18,8 45 +treeBig 11,-10 135 +treeSmall 16,5 180 +treeBig -13,-17 330 +treeSmall -2,14 270 +#treeBig 7,9 300 +treeSmall 23,-10 240 +treeBig -6,18 180 +treeSmall 5,27 270 +treeBig 14,-11 60 +treeSmall 9,-16 180 +treeBig -12,22 240 +treeBig 18,7 360 +treeSmall -24,-4 200 +treeBig -8,21 300 +treeSmall 12,-19 120 +treeBig 6,-12 180 +treeSmall -11,10 75 +treeBig 9,6 270 +treeSmall 8,-14 150 +treeBig 3,18 30 +treeSmall 17,13 100 +treeBig -9,20 90 +treeBig 6,-22 330 +treeSmall -20,7 45 +treeBig 21,11 150 +treeSmall 15,-18 270 +treeBig -3,-12 200 +treeBig 12,-28 330 +treeSmall -17,-7 120 +treeBig -10,9 300 +treeSmall 2,-14 240 +treeBig 24,2 360 +treeSmall 4,-13 300 +treeBig -19,20 90 +#treeSmall -11,5 45 +treeBig 15,9 180 +treeSmall -6,10 240 +treeBig 3,15 30 +treeSmall 9,-19 150 +treeBig -21,-4 330 +treeSmall 19,11 270 +treeSmall 12,24 110 +treeBig -13,15 45 +treeSmall 7,-15 240 +treeBig 26,-8 300 +treeSmall -16,14 120 +treeBig 14,18 360 +treeSmall 8,21 100 +treeBig -8,-18 240 +treeSmall 9,15 180 +treeBig 10,-20 270 +treeSmall 2,27 90 +treeBig 18,12 300 +treeSmall -10,-14 150 +treeBig -15,16 330 +treeSmall -9,19 45 +treeBig 17,-14 120 +treeSmall 5,-25 180 +treeBig 7,23 30 +treeSmall -14,-12 200 +treeBig 6,-16 300 +treeSmall -20,-8 100 +treeBig 4,11 240 +treeSmall 24,-15 90 +treeSmall -19,-19 360 +treeBig 20,8 45 +treeSmall 3,22 270 +treeBig 13,-9 180 +treeSmall -11,18 150 +treeBig -17,-4 300 +treeSmall 5,-14 240 +treeBig 9,17 330 +treeSmall 15,13 90 +treeBig -21,18 30 +treeSmall 6,20 100 +treeBig -16,22 180 +treeSmall -5,18 360 +treeBig 22,11 45 +treeSmall 10,-23 240 +treeBig -10,-16 300 +treeSmall -17,14 120 +treeBig 20,4 150 +treeSmall 11,-22 180 +treeBig -24,-11 200 +treeSmall 14,17 150 +treeBig -8,-12 300 +treeSmall 7,-18 100 +treeBig -5,16 330 +treeSmall 16,-14 200 +treeBig 18,-8 90 +treeSmall -23,-9 45 +treeBig 24,10 300 +treeSmall -4,19 180 +treeBig 12,-5 330 +treeSmall -19,16 100 +treeBig 14,20 150 +treeSmall 9,12 180 +treeBig -22,8 60 +treeSmall 6,18 360 +treeBig 25,-9 45 +treeBig -10,12 240 +treeSmall 19,-17 100 +treeSmall -13,19 90 +treeSmall 16,-12 120 +treeBig 22,-6 45 +treeSmall -18,15 200 +treeBig 14,-10 300 +treeBig 6,10 330 +treeSmall 17,18 90 +treeBig -20,4 180 +treeBig 19,-16 300 +treeSmall -15,9 270 +treeBig 12,22 360 + + + + diff --git a/Projekte/mdga/client/src/main/resources/Models/treeBig/treeBig_diff.png b/Projekte/mdga/client/src/main/resources/Models/treeBig/treeBig_diff.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b45e9199aaf0eef86a20edc54881d11f2ae8b6a3 100644 GIT binary patch literal 95468 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%0AB)Fl(vk&rtR(X! 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 b05qjP=d8dHloYbwNIe?rndoA!yZ!TjkFR0G literal 0 HcmV?d00001 From e9ba888651c44da257548bb834198e9a7dc57beb Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 22:45:44 +0100 Subject: [PATCH 44/82] changed the logic so that the isHost is not decided by the client and instead by the server --- .../java/pp/mdga/client/view/MainView.java | 4 +- .../java/pp/mdga/client/ClientGameLogic.java | 38 +++++++++++++++++ .../java/pp/mdga/client/DialogsState.java | 41 ------------------- .../mdga/client/dialogState/LobbyState.java | 14 +++---- .../dialogState/NetworkDialogState.java | 2 + .../client/dialogState/StartDialogState.java | 26 +----------- .../RollRankingDiceState.java | 2 +- 7 files changed, 51 insertions(+), 76 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java index df534142..e19fd274 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java @@ -100,6 +100,7 @@ private void mainMenu() { private void tryHost() { int port = 0; String text = hostDialog.getPort(); + app.getGameLogic().selectHost(""); try { port = Integer.parseInt(text); @@ -113,7 +114,6 @@ private void tryHost() { } hostDialog.connectServerAsClient(); app.getModelSynchronize().setHost(port); - app.getGameLogic().selectHost(""); //app.getAcousticHandler().playSound(MdgaSound.WRONG_INPUT); return; } @@ -128,6 +128,7 @@ private void tryJoin() { int port = 0; String ip = joinDialog.getIpt(); String portText = joinDialog.getPort(); + app.getGameLogic().selectJoin(""); try { // Validate the port @@ -141,7 +142,6 @@ private void tryJoin() { app.getModelSynchronize().setName(startDialog.getName()); joinDialog.setHostname(ip); joinDialog.connectToServer(); - app.getGameLogic().selectJoin(""); return; } } catch (IllegalArgumentException e) { diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java index e27a7d49..5a7cc107 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java @@ -25,6 +25,8 @@ public class ClientGameLogic implements ServerInterpreter { private ClientState state; private final ArrayList notifications = new ArrayList<>(); private boolean isHost; + private int ownPlayerID; + private String ownPlayerName; private final DialogsState dialogsState = new DialogsState(null, this); private final GameState gameState = new GameState(null, this); @@ -79,6 +81,42 @@ public ClientSender getClientSender(){ return clientSender; } + /** + * This method is used to get the ownPlayerId + * + * @return the ownPlayerId + */ + public int getOwnPlayerId() { + return ownPlayerID; + } + + /** + * This method is used to get the ownPlayerName + * + * @return the ownPlayerName + */ + public String getOwnPlayerName() { + return ownPlayerName; + } + + /** + * This method is used to set the ownPlayerName + * + * @param ownPlayerName the ownPlayerName to be set + */ + public void setOwnPlayerName(String ownPlayerName) { + this.ownPlayerName = ownPlayerName; + } + + /** + * This method is used to set the ownPlayerId + * + * @param ownPlayerId the ownPlayerId to be set + */ + public void setOwnPlayerId(int ownPlayerId) { + this.ownPlayerID = ownPlayerId; + } + /** * This method returns the game * diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index ef2dccdf..044be708 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -11,9 +11,6 @@ public class DialogsState extends ClientState { private DialogStates currentState; - private int ownPlayerID; - private String ownPlayerName; - private final LobbyState lobbyState = new LobbyState(this, logic); private final NetworkDialogState networkDialogState = new NetworkDialogState(this, logic); private final StartDialogState startDialogState = new StartDialogState(this, logic); @@ -42,8 +39,6 @@ public void exit(){ @Override public void enter(){ setState(startDialogState); - ownPlayerID = 0; - ownPlayerName = null; } /** @@ -59,42 +54,6 @@ public void setState(DialogStates newState){ currentState = newState; } - /** - * This method is used to get the ownPlayerId - * - * @return the ownPlayerId - */ - public int getOwnPlayerId() { - return ownPlayerID; - } - - /** - * This method is used to get the ownPlayerName - * - * @return the ownPlayerName - */ - public String getOwnPlayerName() { - return ownPlayerName; - } - - /** - * This method is used to set the ownPlayerName - * - * @param ownPlayerName the ownPlayerName to be set - */ - public void setOwnPlayerName(String ownPlayerName) { - this.ownPlayerName = ownPlayerName; - } - - /** - * This method is used to set the ownPlayerId - * - * @param ownPlayerId the ownPlayerId to be set - */ - public void setOwnPlayerId(int ownPlayerId) { - this.ownPlayerID = ownPlayerId; - } - /** * This method is used to get the lobbyState * diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index fb838357..f40d4770 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -27,7 +27,7 @@ public LobbyState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - logic.send(new JoinedLobbyMessage(parent.getOwnPlayerName())); + logic.send(new JoinedLobbyMessage(logic.getOwnPlayerName())); } @Override @@ -74,7 +74,7 @@ public void selectStart(){ @Override public void received(ServerStartGameMessage msg){ - logic.addNotification(new GameNotification(logic.getGame().getPlayerById(parent.getOwnPlayerId()).getColor())); + logic.addNotification(new GameNotification(logic.getGame().getPlayerById(logic.getOwnPlayerId()).getColor())); for (Map.Entry entry : logic.getGame().getBoard().getPlayerData().entrySet()) { List pieceList = new ArrayList<>(); for(Piece piece : entry.getValue().getPieces()){ @@ -88,21 +88,21 @@ public void received(ServerStartGameMessage msg){ @Override public void received(LobbyPlayerJoinedMessage msg){ - if(msg.getPlayer().getName().equals(parent.getOwnPlayerName())){ - parent.setOwnPlayerId(msg.getId()); + if(msg.getPlayer().getName().equals(logic.getOwnPlayerName())){ + logic.setOwnPlayerId(msg.getId()); } - if (msg.isHost() && msg.getId() == parent.getOwnPlayerId()){ + if (msg.isHost() && msg.getId() == logic.getOwnPlayerId()){ logic.setHost(true); } - logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(parent.getOwnPlayerName()))); + logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(logic.getOwnPlayerName()))); logic.getGame().getPlayers().put(msg.getId(), msg.getPlayer()); } @Override public void received(UpdateTSKMessage msg){ if(msg.isTaken()) { - logic.addNotification(new TskSelectNotification(msg.getColor(), logic.getGame().getPlayers().get(msg.getId()).getName(), parent.getOwnPlayerId()== msg.getId())); + logic.addNotification(new TskSelectNotification(msg.getColor(), logic.getGame().getPlayers().get(msg.getId()).getName(), logic.getOwnPlayerId()== msg.getId())); } else { logic.addNotification(new TskUnselectNotification(logic.getGame().getPlayers().get(msg.getId()).getColor())); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java index a5ef0cba..a6a206d6 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java @@ -53,6 +53,8 @@ public void selectLeave() { */ @Override public void received(LobbyAcceptMessage msg) { + logic.getGame().setHost(msg.getHost()); + logic.setHost(logic.getGame().isHost()); parent.setState(parent.getLobby()); logic.addNotification(new LobbyDialogNotification()); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java index 618387d2..6c49408b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/StartDialogState.java @@ -32,30 +32,6 @@ public void enter() { public void exit() { } - /** - * Select the join option - * - * @param name the name - */ - @Override - public void selectJoin(String name) { - parent.setOwnPlayerName(name); - parent.setState(parent.getNetworkDialog()); - logic.setHost(false); - } - - /** - * Select the host option - * - * @param name the name - */ - @Override - public void selectHost(String name) { - parent.setOwnPlayerName(name); - parent.setState(parent.getLobby()); - logic.setHost(true); - } - /** * Set the name * @@ -63,8 +39,8 @@ public void selectHost(String name) { */ @Override public void setName(String name) { + logic.setOwnPlayerName(name); parent.setState(parent.getNetworkDialog()); - parent.setOwnPlayerName(name); } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java index e6a98522..59cba3ec 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java @@ -34,7 +34,7 @@ public void selectDice(){ @Override public void received(DieMessage msg){ - logic.addNotification(new RollDiceNotification(logic.getGame().getPlayerById(logic.getDialogs().getOwnPlayerId()).getColor(), msg.getDiceEye(),true)); + logic.addNotification(new RollDiceNotification(logic.getGame().getPlayerById(logic.getOwnPlayerId()).getColor(), msg.getDiceEye(),true)); parent.setState(parent.getWaitRanking()); } } From 252c37ae9ad8f69032df9e1e707ed2a742990193 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 22:58:46 +0100 Subject: [PATCH 45/82] Updated 'LobbyState' class. Updated the 'LobbyState' class by sending the 'ServerStartGameMessage' with a 'Board' object. --- .../src/main/java/pp/mdga/server/automaton/LobbyState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 9093dd90..10c7bfe4 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -125,7 +125,7 @@ public void received(LobbyReadyMessage msg, int from) { this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, true)); if (this.logic.getGame().areAllReady()) { this.initializeGame(); - this.logic.getServerSender().broadcast(new ServerStartGameMessage()); + this.logic.getServerSender().broadcast(new ServerStartGameMessage(this.logic.getGame().getBoard())); this.logic.setCurrentState(this.logic.getGameState()); } } @@ -171,7 +171,7 @@ public void received(LeaveGameMessage msg, int from) { public void received(StartGameMessage msg, int from) { if (msg.isForceStartGame() || this.logic.getGame().areAllReady()) { this.initializeGame(); - this.logic.getServerSender().broadcast(new ServerStartGameMessage()); + this.logic.getServerSender().broadcast(new ServerStartGameMessage(this.logic.getGame().getBoard())); this.logic.setCurrentState(this.logic.getGameState()); } } From 3f49b432c44001b7c00c3e1ce6a785b048ab1e45 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Mon, 2 Dec 2024 22:58:52 +0100 Subject: [PATCH 46/82] added SSAO and FXAA --- .../main/java/pp/mdga/client/board/CameraHandler.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java index 57da8393..82e333d5 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java @@ -7,6 +7,8 @@ import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.FXAAFilter; +import com.jme3.post.ssao.SSAOFilter; import com.jme3.scene.Spatial; import com.jme3.shadow.DirectionalLightShadowFilter; import com.jme3.shadow.EdgeFilteringMode; @@ -37,6 +39,8 @@ public class CameraHandler { private Color ownColor; private boolean init; private boolean initRot; + private SSAOFilter ssaoFilter; + private FXAAFilter fxaaFilter; /** * Constructor for the CameraHandler. Initializes the camera settings and lighting. @@ -65,6 +69,9 @@ public CameraHandler(MdgaApp app, FilterPostProcessor fpp) { dlsf.setEnabled(true); dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); dlsf.setShadowIntensity(0.7f); + ssaoFilter = new SSAOFilter(6, 10f, 0.33f, 0.61f); +// ssaoFilter = new SSAOFilter(); + fxaaFilter = new FXAAFilter(); sky = SkyFactory.createSky(app.getAssetManager(), "Images/sky/sky.dds", EnvMapType.EquirectMap).rotate(FastMath.HALF_PI*1,0,FastMath.HALF_PI*0.2f); @@ -82,6 +89,8 @@ public void init(Color ownColor) { app.getRootNode().addLight(ambient); app.getRootNode().attachChild(sky); fpp.addFilter(dlsf); + fpp.addFilter(ssaoFilter); + fpp.addFilter(fxaaFilter); init = true; initRot = true; this.ownColor = ownColor; From fb6cbeaaf5cc5f596a8497451e7df5c64cb20018 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:00:39 +0100 Subject: [PATCH 47/82] Updated 'ServerStartGameMessage' class. Updated the 'ServerStartGameMessage' class by adding the 'board' attribute and its getter method to it. --- .../server/ServerStartGameMessage.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java index 69e3fb96..fa11b561 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java @@ -8,13 +8,38 @@ */ @Serializable public class ServerStartGameMessage extends ServerMessage { + /** + * Create ServerStartGameMessage attributes. + */ + private final Board board; /** * Constructs a new ServerStartGame instance. */ public ServerStartGameMessage() { super(); + this.board = new Board(); } + + /** + * Constructor. + * + * @param board as the board of the game as a Board object. + */ + public ServerStartGameMessage(Board board) { + super(); + this.board = board; + } + + /** + * This method will return board attribute of ServerStartGameMessage class. + * + * @return board as a Board object. + */ + public Board getBoard() { + return this.board; + } + /** * Accepts a visitor to process this message. * From 659d69d3eb65ecc4f2e0ce3ba05ec715ad2b45e0 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:06:44 +0100 Subject: [PATCH 48/82] Updated 'GameState' class. Updated the 'GameState' class by sending a broadcast message after a player left the game and only one player is remaining. --- .../model/src/main/java/pp/mdga/server/automaton/GameState.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/GameState.java index d9ac8431..c6368f05 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/GameState.java @@ -4,6 +4,7 @@ import pp.mdga.message.client.DisconnectedMessage; import pp.mdga.message.client.LeaveGameMessage; import pp.mdga.message.client.RequestDieMessage; +import pp.mdga.message.server.CeremonyMessage; import pp.mdga.message.server.PauseGameMessage; import pp.mdga.server.automaton.game.AnimationState; import pp.mdga.server.automaton.game.DetermineStartPlayerState; @@ -82,6 +83,7 @@ public void received(DisconnectedMessage msg, int from) { public void received(LeaveGameMessage msg, int from) { this.logic.getGame().updatePlayerActiveState(from, false); if (this.logic.getGame().getNumberOfActivePlayers() == 1) { + this.logic.getServerSender().broadcast(new CeremonyMessage()); this.logic.setCurrentState(this.logic.getCeremonyState()); } } From 4e6a272e7a93352e717b119b53027db68b38d56e Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 23:08:56 +0100 Subject: [PATCH 49/82] added that when the client is in the game state and recieves the ceremony message it always changes to ceremony state --- .../mdga/model/src/main/java/pp/mdga/client/GameState.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java index 48da8a6c..fd47e32e 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java @@ -192,7 +192,8 @@ public void received(MoveMessage msg){ */ @Override public void received(CeremonyMessage msg){ - state.received(msg); + logic.addNotification(createCeremonyNotification()); + logic.setState(logic.getCeremony()); } /** From a1d10521aca0c5ece41d734a6298f68c1e23c8cf Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:14:29 +0100 Subject: [PATCH 50/82] Updated 'ServerSener' interface. Updated the 'ServerSender' interface by adding the 'shutdown' method to it. --- .../model/src/main/java/pp/mdga/server/ServerSender.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/ServerSender.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/ServerSender.java index ac0b183d..055d1019 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/ServerSender.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/ServerSender.java @@ -28,4 +28,9 @@ public interface ServerSender { * @param id as the connection id of the client as an Integer. */ void disconnectClient(int id); + + /** + * This method will be used to shut down the server. + */ + void shutdown(); } From 002a42be38c4a88855a1776adcaac8d0f8839722 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:21:42 +0100 Subject: [PATCH 51/82] Updated 'LobbyState' class. Updated the 'LobbyState' class by removing the 'received(LeaveGameMessage msg, int from)' from it. --- .../pp/mdga/server/automaton/LobbyState.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 10c7bfe4..caf08fd4 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -143,23 +143,6 @@ public void received(LobbyNotReadyMessage msg, int from) { this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, false)); } - /** - * This method will be called whenever the server received an LeaveGameMessage message. - * It will also get the client id of the player who send this message. - * - * @param msg as the message which was sent by the player as a LeaveGameMessage object. - * @param from as the client id of the player as an Integer. - */ - @Override - public void received(LeaveGameMessage msg, int from) { - if (from == this.logic.getGame().getHost()) { - this.logic.getServerSender().broadcast(new ShutdownMessage()); - } - this.logic.getGame().removePlayer(from); - this.logic.getServerSender().broadcast(new LobbyPlayerLeaveMessage(from)); - this.logic.getServerSender().disconnectClient(from); - } - /** * This method will be called whenever the server received a StartGame message. * It will also get the client id of the player who send this message. From 838f59b9aaff1893ca5351c94862b41b246a9d84 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:22:45 +0100 Subject: [PATCH 52/82] Updated 'ServerState' class. Updated the 'ServerState' class by filling the 'received(LeaveGameMessage msg, int from)' in it. --- .../java/pp/mdga/server/automaton/ServerState.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java index da9ebc44..0a718927 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/ServerState.java @@ -1,6 +1,8 @@ package pp.mdga.server.automaton; import pp.mdga.message.client.*; +import pp.mdga.message.server.LobbyPlayerLeaveMessage; +import pp.mdga.message.server.ShutdownMessage; import pp.mdga.server.ServerGameLogic; /** @@ -75,7 +77,15 @@ public void received(JoinedLobbyMessage msg, int from) {} * @param msg as the message which was sent by the player as a LeaveGameMessage object. * @param from as the client id of the player as an Integer. */ - public void received(LeaveGameMessage msg, int from) {} + public void received(LeaveGameMessage msg, int from) { + if (from == this.logic.getGame().getHost()) { + this.logic.getServerSender().broadcast(new ShutdownMessage()); + this.logic.getServerSender().shutdown(); + } + this.logic.getGame().removePlayer(from); + this.logic.getServerSender().broadcast(new LobbyPlayerLeaveMessage(from)); + this.logic.getServerSender().disconnectClient(from); + } /** * This method will be called whenever the server received a LobbyReadyMessage message. From 1f64676d312f3f33593e00d8ebfae80a7db10b05 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Mon, 2 Dec 2024 23:23:56 +0100 Subject: [PATCH 53/82] Updated 'MdgaServer' class. Updated the 'MdgaServer' class by adding the 'shutdown' message to it. --- .../java/pp/mdga/client/server/MdgaServer.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index 97295da2..204e1138 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -296,4 +296,21 @@ public void broadcast(ServerMessage message) { public void disconnectClient(int id) { this.myServer.getConnection(id).close(""); } + + /** + * This method will be used to shut down the server. + * It will iterate threw all connections of myServer attribute and check if they are equal to null. If not they will + * be closed. After that the myServer attribute will be closed and this program will be exited with the exit code 0. + */ + @Override + public void shutdown() { + for (HostedConnection client : this.myServer.getConnections()) { + if (client != null) { + client.close("Host closed the server."); + } + } + + this.myServer.close(); + this.exit(0); + } } From eea566cc8b478596a81d89a1d1f3c331777a209b Mon Sep 17 00:00:00 2001 From: Fleischer Hanno Date: Mon, 2 Dec 2024 23:25:54 +0100 Subject: [PATCH 54/82] added the logic for server shutdown --- .../src/main/java/pp/mdga/client/ClientGameLogic.java | 7 ++++++- Projekte/mdga/model/src/main/resources/mdga.properties | 1 + Projekte/mdga/model/src/main/resources/mdga_de.properties | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java index 5a7cc107..8829e43f 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java @@ -1,5 +1,6 @@ package pp.mdga.client; +import pp.mdga.Resources; import pp.mdga.game.BonusCard; import pp.mdga.game.Color; import pp.mdga.game.Game; @@ -398,7 +399,11 @@ public void received(ServerStartGameMessage msg) { * @param msg the SelectTSK message received. */ @Override - public void received(ShutdownMessage msg) {state.received(msg);} + public void received(ShutdownMessage msg) { + addNotification(new InfoNotification(Resources.stringLookup("server.shutdown"))); + addNotification(new StartDialogNotification()); + setState(dialogsState); + } /** * Handles a IncorrectRequest message received from the server. diff --git a/Projekte/mdga/model/src/main/resources/mdga.properties b/Projekte/mdga/model/src/main/resources/mdga.properties index fc9ab8ae..ea216f91 100644 --- a/Projekte/mdga/model/src/main/resources/mdga.properties +++ b/Projekte/mdga/model/src/main/resources/mdga.properties @@ -1,4 +1,5 @@ lobby.deny.join=The lobby is already full. +server.shutdown=The server has shut down. incorrect.request.0=The selected TSK is already occupied. incorrect.request.1=No TSK is available for selection. diff --git a/Projekte/mdga/model/src/main/resources/mdga_de.properties b/Projekte/mdga/model/src/main/resources/mdga_de.properties index dc3490b0..81cc4ad2 100644 --- a/Projekte/mdga/model/src/main/resources/mdga_de.properties +++ b/Projekte/mdga/model/src/main/resources/mdga_de.properties @@ -1,4 +1,5 @@ lobby.deny.join=Die Lobby ist bereits voll. +server.shutdown=Der Server wurde heruntergefahren. incorrect.request.1=Die ausgewählte TSK ist bereits belegt. incorrect.request.2=Es gibt keine freie TSK mehr, welche ausgewählt werden kann. From abe66aff5dbf9911d79cbf33fd048ffcf4786c33 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:37:24 +0100 Subject: [PATCH 55/82] Updated 'LobbyState' class. Updated 'LobbyState' class by removed unused imports in it. --- .../model/src/main/java/pp/mdga/server/automaton/LobbyState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index caf08fd4..ad87ba3b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -7,7 +7,6 @@ import pp.mdga.message.server.*; import pp.mdga.server.ServerGameLogic; -import java.util.ArrayList; import java.util.Map; /** From 5b9bc7aa361552142b26ea36c401cd5bd8c6a32b Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:38:24 +0100 Subject: [PATCH 56/82] Updated 'Piece' class. Updated the 'Piece' class by removing the unused 'id' parameter from the constructor. --- Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java index a01c1247..8ddb376c 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java @@ -35,12 +35,15 @@ public class Piece { * @param color the color of the piece * @param state the state of the piece */ - public Piece(Color color, PieceState state, int id) { + public Piece(Color color, PieceState state) { this.color = color; this.state = state; shield = ShieldState.NONE; } + /** + * Constructor. + */ private Piece() { color = Color.NONE; state = PieceState.WAITING; From 5cf9746931022cf107127deeb84a7a2c14d0fe28 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:48:39 +0100 Subject: [PATCH 57/82] Updated 'PieceState' enumeration. Updated the 'PieceState' enumeration by removing unused methods from it. --- .../src/main/java/pp/mdga/game/PieceState.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java index 780d8df0..24e12ddd 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java @@ -1,7 +1,5 @@ package pp.mdga.game; -import com.jme3.network.serializing.Serializable; - /** * Represents the state of a piece. */ @@ -22,20 +20,4 @@ public enum PieceState { * The piece is finished. */ HOMEFINISHED; - - PieceState(){ - - } - - public static PieceState getPieceStateByIndex(int index){ - if (index < 0 || index >= values().length) { - throw new IllegalArgumentException(""); - } - return values()[index]; - } - - - public PieceState next() { - return values()[(ordinal() + 1) % values().length]; - } } From 1870d4fe0ea15f4b7f4993c05a4bb716a050a447 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:49:09 +0100 Subject: [PATCH 58/82] Updated 'ShieldState' enumeration. Updated the 'ShieldState' enumeration by removing unused methods from it. --- .../main/java/pp/mdga/game/ShieldState.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java index 19a09a5d..78eee486 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java @@ -1,11 +1,8 @@ package pp.mdga.game; -import com.jme3.network.serializing.Serializable; - /** * Represents the state of a piece's shield. */ -@Serializable public enum ShieldState { /** * The shield is not active. @@ -19,19 +16,4 @@ public enum ShieldState { * The shield is suppressed, when the piece is on a start node. */ SUPPRESSED; - - ShieldState(){ - - } - - public static ShieldState getShieldStateByIndex(int index){ - if (index < 0 || index >= values().length) { - throw new IllegalArgumentException(""); - } - return values()[index]; - } - - public ShieldState next() { - return values()[(ordinal() + 1) % values().length]; - } } From 3a02edb944b7aea1daac26f3909a3468cf432397 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:50:53 +0100 Subject: [PATCH 59/82] Updated 'PlayerData' class. Updated the 'PlayerData' class by updating the 'Piece' creation inside the constructor. --- Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java index ae3387e4..809b9d66 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java @@ -39,7 +39,7 @@ public PlayerData(Color color) { waitingArea = new Piece[Resources.MAX_PIECES]; for (int i = 0; i < Resources.MAX_PIECES; i++) { homeNodes[i] = new HomeNode(); - pieces[i] = new Piece(color, PieceState.WAITING, i); + pieces[i] = new Piece(color, PieceState.WAITING); waitingArea[i] = pieces[i]; } } From 2a84e7cf65dff3998ef519ee94d38be71d7e9811 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 00:51:45 +0100 Subject: [PATCH 60/82] Updated 'Player' class. Updated the 'Player' class by moving all content of 'PlayerData' class in 'Player' class. --- .../src/main/java/pp/mdga/game/Player.java | 175 +++++++++++++----- 1 file changed, 127 insertions(+), 48 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Player.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Player.java index 0dd875f0..baa88d6f 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Player.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Player.java @@ -1,6 +1,7 @@ package pp.mdga.game; import com.jme3.network.serializing.Serializable; +import pp.mdga.Resources; import java.util.ArrayList; @@ -17,12 +18,12 @@ public class Player { /** * The statistics of the player. */ - private Statistic playerStatistic; + private Statistic playerStatistic = new Statistic(); /** * The hand cards of the player. */ - private ArrayList handCards; + private ArrayList handCards = new ArrayList<>(); /** * The color of the player. @@ -32,13 +33,21 @@ public class Player { /** * Indicates if the player is ready. */ - private boolean isReady; + private boolean ready = false; /** * Indicates if the player is active. */ private boolean active = true; + /** + * Node and piece attributes + */ + private int startNodeIndex = -1; + private HomeNode[] homeNodes = new HomeNode[Resources.MAX_PIECES]; + private Piece[] waitingArea = new Piece[Resources.MAX_PIECES]; + private Piece[] pieces = new Piece[Resources.MAX_PIECES]; + /** * This constructor constructs a new Player object * @@ -46,8 +55,6 @@ public class Player { */ public Player(String name) { this.name = name; - playerStatistic = new Statistic(); - handCards = new ArrayList<>(); } /** @@ -58,39 +65,14 @@ public Player() { } /** - * This method returns the give name of the Player - * - * @return the name of the player as a String + * This method will be used to initialize all nodes and pieces of the Player class. */ - public String getName() { - return name; - } - - /** - * This method sets the name of the player - * - * @param name the new name of the player - */ - public void setName(String name) { - this.name = name; - } - - /** - * This methode returns the statistics of the player - * - * @return the statistics of the player - */ - public Statistic getPlayerStatistic() { - return playerStatistic; - } - - /** - * This method returns the current handCards of the player - * - * @return the handCards of the player - */ - public ArrayList getHandCards() { - return handCards; + public void initialize() { + for (int index = 0; index < Resources.MAX_PIECES; index++) { + this.homeNodes[index] = new HomeNode(); + this.pieces[index] = new Piece(this.color, PieceState.WAITING); + this.waitingArea[index] = this.pieces[index]; + } } /** @@ -116,6 +98,58 @@ public BonusCard removeHandCard(BonusCard card) { return cardToRemove; } + /** + * This method will be used to add the given piece parameter to the first free slot inside the waitingArea attribute + * of Player class. + * + * @param piece as the piece which should be added to the waitingArea attribute of Player class as a Piece object. + */ + public void addWaitingPiece(Piece piece) { + for (int i = 0; i < Resources.MAX_PIECES; i++) { + if (this.waitingArea[i] == null) { + this.waitingArea[i] = piece; + return; + } + } + } + + /** + * This method will be used to check if the player is finished. + * ToDo: Currently return always false. Implement logic! + * + * @return true or false. + */ + public boolean isFinished() { + return false; + } + + /** + * This method returns the give name of the Player + * + * @return the name of the player as a String + */ + public String getName() { + return name; + } + + /** + * This methode returns the statistics of the player + * + * @return the statistics of the player + */ + public Statistic getPlayerStatistic() { + return playerStatistic; + } + + /** + * This method returns the current handCards of the player + * + * @return the handCards of the player + */ + public ArrayList getHandCards() { + return handCards; + } + /** * This method returns the color of the player * @@ -125,22 +159,13 @@ public Color getColor() { return color; } - /** - * This method sets the color of the player - * - * @param color the new color of the player - */ - public void setColor(Color color) { - this.color = color; - } - /** * This method returns if the player is ready * * @return true if the player is ready, false otherwise */ public boolean isReady() { - return isReady; + return ready; } /** @@ -152,13 +177,67 @@ public boolean isActive() { return this.active; } + /** + * This method will be used to return startNodeIndex of Player class. + * + * @return startNodeIndex as an Integer. + */ + public int getStartNodeIndex() { + return this.startNodeIndex; + } + + /** + * This method will be used to return homeNodes attribute of Player class. + * + * @return homeNodes as an Array of Node objects. + */ + public Node[] getHomeNodes() { + return this.homeNodes; + } + + /** + * This method will be used to return waitingArea attribute of Player class. + * + * @return waitingArea as an Array of Piece objects. + */ + public Piece[] getWaitingArea() { + return this.waitingArea; + } + + /** + * This method will be used to return pieces attribute of Player class. + * + * @return pieces as an Array of Piece objects. + */ + public Piece[] getPieces() { + return this.pieces; + } + + /** + * This method sets the name of the player + * + * @param name the new name of the player + */ + public void setName(String name) { + this.name = name; + } + + /** + * This method sets the color of the player + * + * @param color the new color of the player + */ + public void setColor(Color color) { + this.color = color; + } + /** * This method sets the player to ready * * @param ready true if the player is ready, false otherwise */ public void setReady(boolean ready) { - isReady = ready; + this.ready = ready; } /** From c707abc4651914b3d9e242e1dcbdcd64a34a4dd2 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 01:04:02 +0100 Subject: [PATCH 61/82] Updated 'Die' class. Updated the 'Die' class by adding another constructor for test cases to it. --- .../model/src/main/java/pp/mdga/game/Die.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Die.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Die.java index 08a378e2..fc013d75 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Die.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Die.java @@ -1,7 +1,11 @@ package pp.mdga.game; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.random.RandomGenerator; import java.util.random.RandomGeneratorFactory; +import java.util.stream.IntStream; /** * This class represents a simple die with the possibilities to shuffle and return the roll result. @@ -43,6 +47,14 @@ public class Die { */ private int lastNumberOfDice; + /** + * Predefined rolls for testing. + */ + private Iterator rolls; + + /** + * If the results of the die should be locked. + */ private final boolean lock; /** @@ -65,7 +77,7 @@ public Die(long seed) { /** * Constructor. - * + * * @param roll as the roll which should be returned everytime the shuffle method will be called as an Integer. */ public Die(int roll) { @@ -74,6 +86,17 @@ public Die(int roll) { this.lock = true; } + /** + * Constructor. + * + * @param rolls as a variable list of rolls which will be used by test cases as an Array of Integers. + */ + public Die(int... rolls) { + this.random = RandomGeneratorFactory.of("Random").create(); + this.rolls = IntStream.of(rolls).iterator(); + this.lock = false; + } + /** * This method will be used to return a random number generated by the random attribute of Die class. * @@ -84,6 +107,11 @@ public int shuffle() { return this.lastNumberOfDice; } + if (this.rolls != null && this.rolls.hasNext()) { + this.lastNumberOfDice = this.rolls.next(); + return this.lastNumberOfDice; + } + this.lastNumberOfDice = this.random.nextInt(MAXIMUM_EYES) + 1; return this.lastNumberOfDice; From 35ab777f044ac35d7b7b325253808bb42af6be88 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 01:30:39 +0100 Subject: [PATCH 62/82] Updated 'DetermineStartPlayerState' class. Updated the 'DetermineStartPlayerState' class by fixing the logic inside the received(RequestDieMessage msg, int from)' method in it. --- .../automaton/game/DetermineStartPlayerState.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java index 42c71629..1c4a23a3 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java @@ -3,6 +3,7 @@ import pp.mdga.game.Player; import pp.mdga.message.client.RequestDieMessage; import pp.mdga.message.server.ActivePlayerMessage; +import pp.mdga.message.server.DiceNowMessage; import pp.mdga.message.server.DieMessage; import pp.mdga.message.server.EndOfTurnMessage; import pp.mdga.server.ServerGameLogic; @@ -48,6 +49,8 @@ public void enter() { @Override public void exit() { LOGGER.log(System.Logger.Level.DEBUG, "Exited DetermineStartPlayerState state."); + this.diceResults.clear(); + this.playersHaveToRoll.clear(); } /** @@ -60,13 +63,12 @@ public void exit() { @Override public void received(RequestDieMessage msg, int from) { int roll = this.logic.getGame().getDie().shuffle(); + this.logic.getServerSender().send(from, new DieMessage(roll)); this.diceResults.put(from, roll); - int maximumRoll = 0; if (this.diceResults.size() == this.logic.getGame().getPlayers().size()) { + int maximumRoll = 0; for (Map.Entry entry: this.diceResults.entrySet()) { - if (maximumRoll == 0) { - maximumRoll = this.diceResults.get(entry.getKey()); - } else if (maximumRoll < entry.getValue()) { + if (maximumRoll <= entry.getValue()) { maximumRoll = entry.getValue(); } else { this.playersHaveToRoll.remove(entry.getKey()); @@ -76,10 +78,12 @@ public void received(RequestDieMessage msg, int from) { if (this.playersHaveToRoll.size() == 1) { this.logic.getServerSender().broadcast(new ActivePlayerMessage(this.logic.getGame().getPlayerById(this.playersHaveToRoll.get(0)).getColor())); + this.gameAutomaton.setCurrentState(this.gameAutomaton.getAnimationState()); } else { for (Integer id: this.playersHaveToRoll) { - this.logic.getServerSender().send(id, new DieMessage(roll)); + this.logic.getServerSender().send(id, new DiceNowMessage()); } + diceResults.clear(); } } } From 5aaf8d4850d1f26eb1689cebec9e6570150cb8c7 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 01:31:22 +0100 Subject: [PATCH 63/82] Updated 'TurnState' class. Updated the 'TurnState' class by setting the start state in it. --- .../src/main/java/pp/mdga/server/automaton/game/TurnState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/TurnState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/TurnState.java index 8a8c0f2c..52a38735 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/TurnState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/TurnState.java @@ -36,6 +36,7 @@ public TurnState(GameState gameAutomaton, ServerGameLogic logic) { this.rollDiceState = new RollDiceState(this, logic); this.choosePieceState = new ChoosePieceState(this, logic); this.movePieceState = new MovePieceState(this, logic); + this.setCurrentState(this.powerCardState); } @Override From a012402a85768130cbb3a4e6012c95900f38e6d1 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 02:15:10 +0100 Subject: [PATCH 64/82] Updated abstract 'TurnAutomatonState' class. Updated the abstract 'TurnAutomatonState' class by updating the JavaDoc text of the constructor. --- .../pp/mdga/server/automaton/game/turn/TurnAutomatonState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java index db0b6989..7485ce58 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java @@ -13,7 +13,7 @@ public abstract class TurnAutomatonState extends ServerState { /** * Constructs a server state of the specified game logic. * - * @param turnAutomaton as the automaton of the turn state as a GameState object. + * @param turnAutomaton as the automaton of the turn state as a TurnState object. * @param logic the game logic */ public TurnAutomatonState(TurnState turnAutomaton, ServerGameLogic logic) { From 3353a890d353c31a3ad1156f68b9fa971345d6b1 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 03:36:22 +0100 Subject: [PATCH 65/82] Updated 'Game' class. Updated the 'Game' class by adding the 'getPlayerIdByColor' method to it. --- .../model/src/main/java/pp/mdga/game/Game.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 515aa1da..199f2481 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -152,6 +152,22 @@ public Player getPlayerByColor(Color color) { return null; } + /** + * This method will be used to return the id of the Player defined by the given color parameter. + * + * @param color as the color of the player as a Color enumeration. + * @return the id of the player as an Integer. + */ + public int getPlayerIdByColor(Color color) { + for (Map.Entry entry : this.players.entrySet()) { + if (entry.getValue().getColor() == color) { + return entry.getKey(); + } + } + + return -1; + } + /** * This method will be used to return the number of active players of this game. * From 79bf1c16e8a2f36fe15fc064200914f932242357 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 03:40:24 +0100 Subject: [PATCH 66/82] Updated 'Game' class. Updated the 'Game' class by adding the 'getActivePlayerId' method to it. --- .../mdga/model/src/main/java/pp/mdga/game/Game.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java index 199f2481..9f0cee8f 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Game.java @@ -152,6 +152,16 @@ public Player getPlayerByColor(Color color) { return null; } + /** + * This method will be used to return the id of the active player depending on the activeColor attribute of Game + * class. + * + * @return the id of the active player as an Integer. + */ + public int getActivePlayerId() { + return this.getPlayerIdByColor(this.activeColor); + } + /** * This method will be used to return the id of the Player defined by the given color parameter. * From 2248d044c151598071e9f1139c1ca8d7ddb51220 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 03:41:58 +0100 Subject: [PATCH 67/82] Updated 'AnimationState' class. Updated the 'AnimationState' class by updating the content inside the 'received(AnimationEndMessage msg, int from) method in it. --- .../server/automaton/game/AnimationState.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/AnimationState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/AnimationState.java index 8d6661b9..c1ba800e 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/AnimationState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/AnimationState.java @@ -11,10 +11,15 @@ */ public class AnimationState extends GameAutomatonState { /** - * Create FirstRollState constants. + * Create AnimationState constants. */ private static final System.Logger LOGGER = System.getLogger(AnimationState.class.getName()); + /** + * Create AnimationState attributes. + */ + private int messageReceived = 0; + /** * Constructs a server state of the specified game logic. * @@ -33,6 +38,7 @@ public void enter() { @Override public void exit() { LOGGER.log(System.Logger.Level.DEBUG, "Exited AnimationState state."); + this.messageReceived = 0; } /** @@ -44,7 +50,11 @@ public void exit() { */ @Override public void received(AnimationEndMessage msg, int from) { - this.logic.getServerSender().send(from, new DiceNowMessage()); - this.gameAutomaton.setCurrentState(this.gameAutomaton.getTurnState()); + if (this.messageReceived == this.logic.getGame().getPlayers().size()) { + this.logic.getServerSender().send(this.logic.getGame().getActivePlayerId(), new DiceNowMessage()); + this.gameAutomaton.setCurrentState(this.gameAutomaton.getTurnState()); + } else { + this.messageReceived++; + } } } From a1e687912a84eb87af2bbad0612dec96de493461 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 04:09:35 +0100 Subject: [PATCH 68/82] Updated abstract 'TurnAutomatonState' class. Updated the abstract 'TurnAutomatonState' class by adding the 'getTurnAutomaton' method to it. --- .../server/automaton/game/turn/TurnAutomatonState.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java index 7485ce58..282d2e50 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/turn/TurnAutomatonState.java @@ -20,4 +20,13 @@ public TurnAutomatonState(TurnState turnAutomaton, ServerGameLogic logic) { super(logic); this.turnAutomaton = turnAutomaton; } + + /** + * This method will be used to return turnAutomaton attribute of TurnAutomatonState object. + * + * @return turnAutomaton as a TurnState object + */ + public TurnState getTurnAutomaton() { + return this.turnAutomaton; + } } From 336f1ec3164c5d2489691e9f38bae2a121e2b37e Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 04:29:16 +0100 Subject: [PATCH 69/82] Updated 'Resources' class. Updated the 'Resources' class by adding the 'MAX_EYES' constant to it. --- Projekte/mdga/model/src/main/java/pp/mdga/Resources.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java index 13a60c48..f1c66018 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/Resources.java @@ -13,6 +13,7 @@ public class Resources { */ public static final int MAX_PLAYERS = 4; public static final int MAX_PIECES = 4; + public static final int MAX_EYES = 6; /** * The resource bundle for the MDGA game. From 0db1f08f3c7463b90dfd6719126d4d64e6a132ab Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 04:49:16 +0100 Subject: [PATCH 70/82] Updated abstract 'GameAutomatonState' class. Updated the abstract 'GameAutomatonState' class by adding the 'getGameAutomaton' method to it. --- .../mdga/server/automaton/game/GameAutomatonState.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/GameAutomatonState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/GameAutomatonState.java index 56eac48f..958f04bb 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/GameAutomatonState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/GameAutomatonState.java @@ -23,4 +23,13 @@ public GameAutomatonState(GameState gameAutomaton, ServerGameLogic logic) { super(logic); this.gameAutomaton = gameAutomaton; } + + /** + * This method will be used to return gameAutomaton attribute of GameAutomatonState object. + * + * @return gameAutomaton as a GameState object. + */ + public GameState getGameAutomaton() { + return this.gameAutomaton; + } } From bb51976127a7ba6923eff0fe71d6db1b7a63e8b0 Mon Sep 17 00:00:00 2001 From: Daniel Grigencha Date: Tue, 3 Dec 2024 04:57:30 +0100 Subject: [PATCH 71/82] Updated 'Node' class. Updated the 'Node' class by overload the 'isOccupied' method in it. --- .../mdga/model/src/main/java/pp/mdga/game/Node.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java index de6530ca..837dd3a6 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java @@ -49,4 +49,14 @@ public void clearOccupant() { public boolean isOccupied() { return occupant != null; } + + /** + * This method will be used to check if the node is occupied by a piece of the given color. + * + * @param color as the color of the piece as a Color object. + * @return true or false. + */ + public boolean isOccupied(Color color) { + return this.occupant != null && this.occupant.getColor() == color; + } } From c4d11ff96171394a3b896f7c0cbae59ccb471ca5 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Tue, 3 Dec 2024 09:15:14 +0100 Subject: [PATCH 72/82] added window title --- .../client/src/main/java/pp/mdga/client/MdgaApp.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index a1df6b46..9755026c 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -2,12 +2,18 @@ import com.jme3.app.SimpleApplication; import com.simsilica.lemur.GuiGlobals; +import com.sun.tools.javac.Main; import pp.mdga.client.acoustic.AcousticHandler; import com.jme3.system.AppSettings; import pp.mdga.client.dialog.JoinDialog; import pp.mdga.client.view.*; +import javax.imageio.ImageIO; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.prefs.Preferences; @@ -78,11 +84,12 @@ public static void main(String[] args) { settings.setHeight(prefs.getInt("height", 720)); settings.setCenterWindow(true); settings.setVSync(false); - + settings.setTitle("MDGA"); MdgaApp app = new MdgaApp(); app.setSettings(settings); app.setShowSettings(false); app.setPauseOnLostFocus(false); + app.start(); } From a0a088a0c46e47ae0894598f506b8209b211f982 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Tue, 3 Dec 2024 15:00:00 +0100 Subject: [PATCH 73/82] Fix minor error in notification processing --- Projekte/.run/MdgaApp.run.xml | 3 +- .../mdga/client/NotificationSynchronizer.java | 57 ++++++++++--------- .../java/pp/mdga/client/view/MdgaView.java | 2 +- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Projekte/.run/MdgaApp.run.xml b/Projekte/.run/MdgaApp.run.xml index 123a07af..4c60627e 100644 --- a/Projekte/.run/MdgaApp.run.xml +++ b/Projekte/.run/MdgaApp.run.xml @@ -1,6 +1,5 @@ - - \ No newline at end of file + diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 1cd3c927..0ffe9d44 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -26,37 +26,42 @@ public void addTestNotification(Notification n) { public void update() { Notification n = app.getGameLogic().getNotification(); - - if(n instanceof InfoNotification infoNotification) { - app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError()); - return; - } - - if(n != null) { - switch (app.getState()) { - case MAIN: - handleMain(n); - break; - case LOBBY: - handleLobby(n); - break; - case GAME: - handleGame(n); - break; - case CEREMONY: - handleCeremony(n); - break; - case NONE: - throw new RuntimeException("no notification expected: " + n.toString()); + while (n != null) { + if(n instanceof InfoNotification infoNotification) { + app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError()); + return; } + + if(n != null) { + switch (app.getState()) { + case MAIN: + handleMain(n); + break; + case LOBBY: + handleLobby(n); + break; + case GAME: + handleGame(n); + break; + case CEREMONY: + handleCeremony(n); + break; + case NONE: + throw new RuntimeException("no notification expected: " + n.getClass().getName()); + } + } + + n = app.getGameLogic().getNotification(); } } private void handleMain(Notification notification) { if (notification instanceof LobbyDialogNotification) { app.enter(MdgaState.LOBBY); + } else if (notification instanceof StartDialogNotification) { + //nothing } else { - throw new RuntimeException("notification not expected: "); + throw new RuntimeException("notification not expected in main: "+ notification.getClass().getName()); } } @@ -75,7 +80,7 @@ private void handleLobby(Notification notification) { app.enter(MdgaState.GAME); ((GameView) app.getView()).setOwnColor(n.getOwnColor()); } else { - throw new RuntimeException("notification not expected: " + notification.toString()); + throw new RuntimeException("notification not expected in lobby: " + notification.getClass().getName()); } } @@ -189,7 +194,7 @@ private void handleGame(Notification notification) { } else if (notification instanceof FinishNotification n){ guiHandler.finish(n.getColorFinished()); } else { - throw new RuntimeException("notification not expected: " + notification.toString()); + throw new RuntimeException("notification not expected in game: " + notification.getClass().getName()); } } @@ -197,7 +202,7 @@ private void handleCeremony(Notification notification) { if (notification instanceof StartDialogNotification) { app.enter(MdgaState.MAIN); } else { - throw new RuntimeException("notification not expected: " + notification.toString()); + throw new RuntimeException("notification not expected in ceremony: " + notification.getClass().getName()); } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java index ebb105b8..e9bb1b65 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MdgaView.java @@ -86,7 +86,7 @@ public void update(float tpf) { videoSettingsDialog.update(); audioSettingsDialog.update(); - if (null != infoLabel && infoTimer.getTimeInSeconds() > 7) { + if (null != infoLabel && infoTimer.getTimeInSeconds() > 5) { infoLabel.hide(); infoLabel = null; } From db50986f3f891d760d9bdfb08d6b26b2bddfdbc6 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Tue, 3 Dec 2024 15:38:13 +0100 Subject: [PATCH 74/82] Fix serialisation issue --- .../src/main/java/pp/mdga/client/server/MdgaServer.java | 5 +++++ Projekte/mdga/model/src/main/java/pp/mdga/game/Color.java | 3 +++ Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java | 2 +- .../mdga/model/src/main/java/pp/mdga/game/PieceState.java | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index 204e1138..0d54e6e7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -2,6 +2,7 @@ import com.jme3.network.*; import com.jme3.network.serializing.Serializer; +import com.jme3.network.serializing.serializers.EnumSerializer; import pp.mdga.Resources; import pp.mdga.game.*; import pp.mdga.message.client.*; @@ -145,6 +146,10 @@ private void initializeSerializables() { Serializer.registerClass(StartNode.class); Serializer.registerClass(PlayerData.class); Serializer.registerClass(HomeNode.class); + + Serializer.registerClass(Color.class, new EnumSerializer()); + Serializer.registerClass(PieceState.class, new EnumSerializer()); + Serializer.registerClass(ShieldState.class, new EnumSerializer()); } private void registerListeners() { diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Color.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Color.java index 828450f4..76d4e17b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Color.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Color.java @@ -1,9 +1,12 @@ package pp.mdga.game; +import com.jme3.network.serializing.Serializable; + /** * This enumeration will be used to show the four different TSK which can be picked from a player. * In Addition, the NONE color will be used to show a none color. */ +@Serializable public enum Color { /** * Represents the air force color. diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java index 837dd3a6..59738c1c 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java @@ -10,7 +10,7 @@ public class Node { protected Piece occupant; public Node(){ - + occupant = new Piece(Color.AIRFORCE, PieceState.WAITING); } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java index 24e12ddd..3c66f677 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java @@ -1,8 +1,11 @@ package pp.mdga.game; +import com.jme3.network.serializing.Serializable; + /** * Represents the state of a piece. */ +@Serializable public enum PieceState { /** * The piece is active. From 69865bb504a7103eb2ae2a97f2bdacc6153c6c75 Mon Sep 17 00:00:00 2001 From: Hanno Fleischer Date: Tue, 3 Dec 2024 16:48:08 +0100 Subject: [PATCH 75/82] added the playeringamenotification to be created from the right dataset --- .../src/main/java/pp/mdga/client/GameState.java | 9 +++++++++ .../java/pp/mdga/client/dialogState/LobbyState.java | 12 +++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java index fd47e32e..34ad5d44 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java @@ -3,8 +3,10 @@ import pp.mdga.client.gameState.*; import pp.mdga.game.BonusCard; import pp.mdga.game.Piece; +import pp.mdga.message.client.LeaveGameMessage; import pp.mdga.message.server.*; import pp.mdga.notification.InterruptNotification; +import pp.mdga.notification.StartDialogNotification; public class GameState extends ClientState { @@ -105,6 +107,13 @@ public void received(PauseGameMessage msg){ logic.addNotification(new InterruptNotification(logic.getGame().getPlayers().get(msg.getPlayerId()).getColor())); } + @Override + public void selectLeave(){ + logic.send(new LeaveGameMessage()); + logic.addNotification(new StartDialogNotification()); + logic.setState(logic.getDialogs()); + } + /** * This method is used to call the received method of the current state * diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index f40d4770..5742998e 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -5,6 +5,7 @@ import pp.mdga.client.DialogsState; import pp.mdga.game.Color; import pp.mdga.game.Piece; +import pp.mdga.game.Player; import pp.mdga.game.PlayerData; import pp.mdga.message.client.*; import pp.mdga.message.server.LobbyPlayerJoinedMessage; @@ -74,14 +75,15 @@ public void selectStart(){ @Override public void received(ServerStartGameMessage msg){ + logic.getGame().setBoard(msg.getBoard()); logic.addNotification(new GameNotification(logic.getGame().getPlayerById(logic.getOwnPlayerId()).getColor())); - for (Map.Entry entry : logic.getGame().getBoard().getPlayerData().entrySet()) { - List pieceList = new ArrayList<>(); + for (Map.Entry entry : logic.getGame().getPlayers().entrySet()) { + entry.getValue().initialize(); + List pieces = new ArrayList<>(); for(Piece piece : entry.getValue().getPieces()){ - System.out.println(piece.getUuid()); - pieceList.add(piece.getUuid()); + pieces.add(piece.getUuid()); } - logic.addNotification(new PlayerInGameNotification(entry.getKey(), pieceList , logic.getGame().getPlayerByColor(entry.getKey()).getName())); + logic.addNotification(new PlayerInGameNotification(entry.getValue().getColor(), pieces, entry.getValue().getName())); } logic.setState(logic.getGameState()); } From 81cb2f33ffc70ebfab422200340cf6febef8a748 Mon Sep 17 00:00:00 2001 From: Hanno Fleischer Date: Tue, 3 Dec 2024 17:56:39 +0100 Subject: [PATCH 76/82] adjusted all constuctors of nodes so that if someone creates a node the piece will be null and the option for a constuctor without arguments is still given for serialization purposes --- Projekte/mdga/model/src/main/java/pp/mdga/game/Board.java | 2 +- .../mdga/model/src/main/java/pp/mdga/game/BonusNode.java | 2 +- .../mdga/model/src/main/java/pp/mdga/game/HomeNode.java | 2 +- Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java | 6 +++++- .../mdga/model/src/main/java/pp/mdga/game/StartNode.java | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Board.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Board.java index b24382fe..b46ef62f 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Board.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Board.java @@ -29,7 +29,7 @@ public Board() { } else if (i == 4 || i == 14 || i == 24 || i == 34) { infield[i] = new BonusNode(); } else { - infield[i] = new Node(); + infield[i] = new Node(null); } } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/BonusNode.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/BonusNode.java index c12f7cab..8ece5b1e 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/BonusNode.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/BonusNode.java @@ -8,6 +8,6 @@ @Serializable public class BonusNode extends Node { BonusNode(){ - super(); + super(null); } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/HomeNode.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/HomeNode.java index 814abaa5..cb373011 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/HomeNode.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/HomeNode.java @@ -8,6 +8,6 @@ @Serializable public class HomeNode extends Node { public HomeNode() { - super(); + super(null); } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java index 59738c1c..921ea970 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Node.java @@ -9,7 +9,11 @@ public class Node { protected Piece occupant; - public Node(){ + public Node(Piece piece){ + occupant = piece; + } + + private Node(){ occupant = new Piece(Color.AIRFORCE, PieceState.WAITING); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java index 24b2c267..c8c79264 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java @@ -18,10 +18,12 @@ public class StartNode extends Node { * @param color the color of the node */ public StartNode(Color color) { + super(null); this.color = color; } private StartNode() { + super(null); color = Color.NONE; } From 7053b163e5411f6b64e45cff09fe1019c0168702 Mon Sep 17 00:00:00 2001 From: Hanno Fleischer Date: Tue, 3 Dec 2024 18:19:55 +0100 Subject: [PATCH 77/82] adjusted LobbyState in the client to use the correct Data --- .../src/main/java/pp/mdga/client/dialogState/LobbyState.java | 3 +-- .../src/main/java/pp/mdga/client/gameState/GameStates.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index 5742998e..06b97e13 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -78,9 +78,8 @@ public void received(ServerStartGameMessage msg){ logic.getGame().setBoard(msg.getBoard()); logic.addNotification(new GameNotification(logic.getGame().getPlayerById(logic.getOwnPlayerId()).getColor())); for (Map.Entry entry : logic.getGame().getPlayers().entrySet()) { - entry.getValue().initialize(); List pieces = new ArrayList<>(); - for(Piece piece : entry.getValue().getPieces()){ + for (Piece piece : logic.getGame().getBoard().getPlayerData().get(entry.getValue().getColor()).getPieces()) { pieces.add(piece.getUuid()); } logic.addNotification(new PlayerInGameNotification(entry.getValue().getColor(), pieces, entry.getValue().getName())); diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/GameStates.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/GameStates.java index b7b80ddc..5bb880d7 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/GameStates.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/GameStates.java @@ -36,8 +36,7 @@ protected void handlePowerCard(PlayCardMessage msg){ logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(enemyPiece)].setOccupant(ownPiece); logic.getGame().getBoard().getInfield()[ownIndex].setOccupant(enemyPiece); } - logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).removeHandCard(msg.getCard()); - logic.getGame().getDiscardPile().add(msg.getCard()); + logic.getGame().getDiscardPile().add(logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).removeHandCard(msg.getCard())); } protected void throwPiece(Piece piece){ From 8e6cb276625d983372911a8b8b24e2973639bbbd Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Wed, 4 Dec 2024 07:53:18 +0100 Subject: [PATCH 78/82] Fix lobbyView ready behavior on tskChange --- .../client/src/main/java/pp/mdga/client/view/LobbyView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java index 31c6a3db..f64623fd 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java @@ -231,6 +231,10 @@ private void toggleTsk(Color color) { break; } + if(isReady) { + setReady(own, false); + } + switch (taken) { case NOT: app.getModelSynchronize().selectTsk(color); From d71f824ca66541f154e65199b8c42eba930ddc92 Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Wed, 4 Dec 2024 09:41:08 +0100 Subject: [PATCH 79/82] Add fullscreen option --- .../src/main/java/pp/mdga/client/MdgaApp.java | 48 ++++++++++++------- .../client/dialog/VideoSettingsDialog.java | 24 +++++++--- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 9755026c..c381a36d 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -45,7 +45,7 @@ public class MdgaApp extends SimpleApplication { private MdgaState state = null; /** Scale for rendering images. */ - private float imageScale = prefs.getInt("scale", 1); + private final float imageScale = prefs.getInt("scale", 1); /** The main menu view. */ private MdgaView mainView; @@ -82,6 +82,7 @@ public static void main(String[] args) { settings.setSamples(128); settings.setWidth(prefs.getInt("width", 1280)); settings.setHeight(prefs.getInt("height", 720)); + settings.setFullscreen(prefs.getBoolean("fullscreen", false)); settings.setCenterWindow(true); settings.setVSync(false); settings.setTitle("MDGA"); @@ -252,30 +253,43 @@ public ServerConnection getNetworkSupport(){ return networkConnection; } - public void updateResolution(int width, int height, float imageFactor) { + public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) { + if(isFullscreen) { + int baseWidth = 1280; + int baseHeight = 720; + float baseAspectRatio = (float) baseWidth / baseHeight; + float newAspectRatio = (float) width / height; + + float scaleFactor = Math.max((float) width / baseWidth, (float) height / baseHeight); + + settings.setFullscreen(true); + + prefs.putFloat("scale", scaleFactor); + prefs.putBoolean("fullscreen", true); + } + prefs.putInt("width", width); prefs.putInt("height", height); prefs.putFloat("scale", imageFactor); - - try { - restartApp(); - } catch (Exception e) { - //nothing - } + prefs.putBoolean("fullscreen", false); } - public static void restartApp() throws IOException { - String javaBin = System.getProperty("java.home") + "/bin/java"; - String classPath = System.getProperty("java.class.path"); - String className = System.getProperty("sun.java.command"); + public static void restartApp() { + try { + String javaBin = System.getProperty("java.home") + "/bin/java"; + String classPath = System.getProperty("java.class.path"); + String className = System.getProperty("sun.java.command"); - ProcessBuilder builder = new ProcessBuilder( - javaBin, "-cp", classPath, className - ); + ProcessBuilder builder = new ProcessBuilder( + javaBin, "-cp", classPath, className + ); - builder.start(); + builder.start(); - System.exit(0); + System.exit(0); + } catch (Exception e) { + throw new RuntimeException("restart failed"); + } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java index cc8f39fa..b1ce3b29 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java @@ -3,13 +3,16 @@ import com.jme3.math.Vector2f; import com.jme3.scene.Node; import pp.mdga.client.MdgaApp; +import pp.mdga.client.button.AbstractButton; import pp.mdga.client.button.ButtonLeft; import pp.mdga.client.button.ButtonRight; import pp.mdga.client.button.MenuButton; import pp.mdga.client.view.MdgaView; public class VideoSettingsDialog extends Dialog { + private ButtonRight fullscreenButton; private MenuButton backButton; + private ButtonRight restartButton; private ButtonLeft hdButton9; private ButtonLeft fullHdButton9; @@ -27,22 +30,25 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) { this.view = view; + fullscreenButton = new ButtonRight(app, node, () -> app.updateResolution(0, 0, 0, true), "Vollbild", 1); backButton = new MenuButton(app, node, view::leaveVideoSettings, "Zurück"); - // MenuButton für verschiedene Auflösungen erstellen - hdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1280, 720, 1.0f), "hd 16:9", 10); - fullHdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1920, 1080, 2.25f), "full hd 16:9", 10); - wqhdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(2560, 1440, 4.0f), "wqhd 16:9", 10); + restartButton = new ButtonRight(app, node, MdgaApp::restartApp, "Neustart", 1); + + hdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1280, 720, 1.0f, false), "hd 16:9", 10); + fullHdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1920, 1080, 2.25f, false), "full hd 16:9", 10); + wqhdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(2560, 1440, 4.0f, false), "wqhd 16:9", 10); - hdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1280, 800, 1.0f), "hd 16:10", 10); - fullHdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1920, 1200, 2.25f), "full hd 16:10", 10); - wqhdButton10 = new ButtonRight(app, node, () -> app.updateResolution(2560, 1600, 4.0f), "wqhd 16:10", 10); + hdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1280, 800, 1.0f, false), "hd 16:10", 10); + fullHdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1920, 1200, 2.25f, false), "full hd 16:10", 10); + wqhdButton10 = new ButtonRight(app, node, () -> app.updateResolution(2560, 1600, 4.0f, false), "wqhd 16:10", 10); float offset = 2.8f; hdButton9.setPos(new Vector2f(hdButton9.getPos().x, MenuButton.VERTICAL - offset)); hdButton10.setPos(new Vector2f(hdButton10.getPos().x, MenuButton.VERTICAL - offset)); + fullscreenButton.setPos(new Vector2f(fullscreenButton.getPos().x, MenuButton.VERTICAL - offset)); offset += 1.5f; fullHdButton9.setPos(new Vector2f(fullHdButton9.getPos().x, MenuButton.VERTICAL - offset)); @@ -68,7 +74,9 @@ protected void onShow() { fullHdButton10.show(); wqhdButton10.show(); + fullscreenButton.show(); backButton.show(); + restartButton.show(); } @Override @@ -83,7 +91,9 @@ protected void onHide() { fullHdButton10.hide(); wqhdButton10.hide(); + fullscreenButton.hide(); backButton.hide(); + restartButton.hide(); } public void update() { From 11d6dd4500b28b7430c3783a7202d7582a5a513e Mon Sep 17 00:00:00 2001 From: Felix Koppe Date: Wed, 4 Dec 2024 10:00:41 +0100 Subject: [PATCH 80/82] Improve video dialog --- .../src/main/java/pp/mdga/client/MdgaApp.java | 10 +++---- .../client/dialog/VideoSettingsDialog.java | 28 +++++++++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index c381a36d..b0120dfb 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -266,12 +266,12 @@ public void updateResolution(int width, int height, float imageFactor, boolean i prefs.putFloat("scale", scaleFactor); prefs.putBoolean("fullscreen", true); + } else { + prefs.putInt("width", width); + prefs.putInt("height", height); + prefs.putFloat("scale", imageFactor); + prefs.putBoolean("fullscreen", false); } - - prefs.putInt("width", width); - prefs.putInt("height", height); - prefs.putFloat("scale", imageFactor); - prefs.putBoolean("fullscreen", false); } public static void restartApp() { diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java index b1ce3b29..5aa98cfe 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/VideoSettingsDialog.java @@ -9,7 +9,11 @@ import pp.mdga.client.button.MenuButton; import pp.mdga.client.view.MdgaView; +import java.util.prefs.Preferences; + public class VideoSettingsDialog extends Dialog { + private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class); + private ButtonRight fullscreenButton; private MenuButton backButton; private ButtonRight restartButton; @@ -30,19 +34,20 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) { this.view = view; - fullscreenButton = new ButtonRight(app, node, () -> app.updateResolution(0, 0, 0, true), "Vollbild", 1); backButton = new MenuButton(app, node, view::leaveVideoSettings, "Zurück"); restartButton = new ButtonRight(app, node, MdgaApp::restartApp, "Neustart", 1); - hdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1280, 720, 1.0f, false), "hd 16:9", 10); - fullHdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(1920, 1080, 2.25f, false), "full hd 16:9", 10); - wqhdButton9 = new ButtonLeft(app, node, () -> app.updateResolution(2560, 1440, 4.0f, false), "wqhd 16:9", 10); + fullscreenButton = new ButtonRight(app, node, () -> updateResolution(0, 0, 0, true), "Vollbild", 1); + + hdButton9 = new ButtonLeft(app, node, () -> updateResolution(1280, 720, 1.0f, false), "hd 16:9", 10); + fullHdButton9 = new ButtonLeft(app, node, () -> updateResolution(1920, 1080, 2.25f, false), "full hd 16:9", 10); + wqhdButton9 = new ButtonLeft(app, node, () -> updateResolution(2560, 1440, 4.0f, false), "wqhd 16:9", 10); - hdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1280, 800, 1.0f, false), "hd 16:10", 10); - fullHdButton10 = new ButtonRight(app, node, () -> app.updateResolution(1920, 1200, 2.25f, false), "full hd 16:10", 10); - wqhdButton10 = new ButtonRight(app, node, () -> app.updateResolution(2560, 1600, 4.0f, false), "wqhd 16:10", 10); + hdButton10 = new ButtonRight(app, node, () -> updateResolution(1280, 800, 1.0f, false), "hd 16:10", 10); + fullHdButton10 = new ButtonRight(app, node, () -> updateResolution(1920, 1200, 2.25f, false), "full hd 16:10", 10); + wqhdButton10 = new ButtonRight(app, node, () -> updateResolution(2560, 1600, 4.0f, false), "wqhd 16:10", 10); float offset = 2.8f; @@ -76,7 +81,6 @@ protected void onShow() { fullscreenButton.show(); backButton.show(); - restartButton.show(); } @Override @@ -101,4 +105,12 @@ public void update() { return; } } + + public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) { + if(width != prefs.getInt("width", 1280) || height != prefs.getInt("height", 720) || isFullscreen != prefs.getBoolean("fullscreen", false)) { + restartButton.show(); + } + + app.updateResolution(width, height, imageFactor, isFullscreen); + } } From 44ef21e6af546c66f4e7f0a2d10a99b04bafc528 Mon Sep 17 00:00:00 2001 From: Cedric Beck Date: Wed, 4 Dec 2024 11:38:35 +0100 Subject: [PATCH 81/82] added handCard Num to playerName display + added remove card in guiHandler --- .../src/main/java/pp/mdga/client/Asset.java | 6 +-- .../pp/mdga/client/InputSynchronizer.java | 13 ++++- .../src/main/java/pp/mdga/client/MdgaApp.java | 2 +- .../mdga/client/NotificationSynchronizer.java | 10 ++-- .../java/pp/mdga/client/gui/CardControl.java | 2 +- .../java/pp/mdga/client/gui/CardLayer.java | 6 ++- .../pp/mdga/client/gui/CardLayerHandler.java | 13 +++++ .../java/pp/mdga/client/gui/GuiHandler.java | 33 ++++++++++-- .../pp/mdga/client/gui/PlayerNameHandler.java | 49 ++++++++++++++++-- .../java/pp/mdga/client/view/GameView.java | 9 ++-- .../src/main/resources/Images/handcard.png | Bin 0 -> 12257 bytes 11 files changed, 118 insertions(+), 25 deletions(-) create mode 100644 Projekte/mdga/client/src/main/resources/Images/handcard.png diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java index 60dfebcb..a24d9d1c 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java @@ -33,11 +33,11 @@ public enum Asset { treeSmall(1.2f), treeBig(1.2f), turboCard, - turboSymbol("Models/turboCard/turboSymbol.j3o", "Models/turboCard/turboCard_diff.j3o"), + turboSymbol("Models/turboCard/turboSymbol.j3o", "Models/turboCard/turboCard_diff.png"), swapCard, - swapSymbol("Models/swapCard/swapSymbol.j3o", "Models/swapCard/swapCard_diff.j3o"), + swapSymbol("Models/swapCard/swapSymbol.j3o", "Models/swapCard/swapCard_diff.png"), shieldCard, - shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.j3o"), + shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.png"), dice ; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index 807a37c4..e0f657da 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -27,6 +27,7 @@ import pp.mdga.notification.SelectableCardsNotification; import java.util.List; +import java.util.UUID; public class InputSynchronizer { @@ -127,7 +128,17 @@ else if(boardSelect != null) { // gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); // gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); // gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4); - gameView.getGuiHandler().showDice(); +// gameView.getGuiHandler().showDice(); +// UUID p1 = UUID.randomUUID(); + +// gameView.getBoardHandler().addPlayer(Color.AIRFORCE,List.of(p1,UUID.randomUUID(),UUID.randomUUID(),UUID.randomUUID())); +// gameView.getBoardHandler().movePieceStartAnim(p1,0); + gameView.getGuiHandler().drawCard(Color.ARMY); + gameView.getGuiHandler().addCardOwn(BonusCard.SHIELD); + gameView.getGuiHandler().playCardOwn(BonusCard.SHIELD); + + + } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index b0120dfb..fbe68143 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -115,7 +115,7 @@ public void simpleInitApp() { gameView = new GameView(this); ceremonyView = new CeremonyView(this); - enter(MdgaState.MAIN); + enter(MdgaState.GAME); } /** diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 0ffe9d44..3e648f28 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -91,7 +91,7 @@ private void handleGame(Notification notification) { ModelSynchronizer modelSynchronizer = app.getModelSynchronize(); if (notification instanceof AcquireCardNotification n) { - guiHandler.addCard(n.getBonusCard()); + guiHandler.addCardOwn(n.getBonusCard()); } else if (notification instanceof ActivePlayerNotification n) { gameView.getGuiHandler().setActivePlayer(n.getColor()); boardHandler.showDice(n.getColor()); @@ -146,12 +146,8 @@ private void handleGame(Notification notification) { } else if (notification instanceof NoShieldNotification n) { boardHandler.unshieldPiece(n.getPieceId()); } else if (notification instanceof PlayCardNotification n) { - switch(n.getCard()){ - case SWAP -> guiHandler.swap(); - case TURBO -> guiHandler.turbo(); - case SHIELD -> guiHandler.shield(); - default -> throw new RuntimeException("invalid card"); - } + if(n.getColor() == gameView.getOwnColor()) guiHandler.playCardOwn(n.getCard()); + else guiHandler.playCardEnemy(n.getColor(), n.getCard()); } else if (notification instanceof PlayerInGameNotification n) { boardHandler.addPlayer(n.getColor(),n.getPiecesList()); guiHandler.addPlayer(n.getColor(),n.getName()); diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardControl.java index f07e7e69..46e47ee6 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardControl.java @@ -60,7 +60,7 @@ private Node createNum(){ Material mat = new Material(getApp().getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Black); circle.setMaterial(mat); - root.attachChild(circle); +// root.attachChild(circle); BitmapFont guiFont = getApp().getAssetManager().loadFont("Fonts/Gunplay.fnt"); num = new BitmapText(guiFont); num.setSize(0.3f); diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayer.java index 860e5324..ebf21b6a 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayer.java @@ -86,7 +86,7 @@ public void render(RenderManager rm) { public void update(float tpf) { if (init && !cardBuffer.isEmpty()) { for (Spatial spatial : cardBuffer) { - root.attachChild(spatial); +// root.attachChild(spatial); } cardBuffer.clear(); } @@ -94,7 +94,9 @@ public void update(float tpf) { } public void addSpatial(Spatial card) { - cardBuffer.add(card); +// cardBuffer.add(card); + root.attachChild(card); + root = root; } public void deleteSpatial(Spatial spatial) { diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java index 116e0987..2d5c4d2d 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java @@ -85,6 +85,19 @@ public void addCard(BonusCard card) { bonusCardControlMap.get(card).setNumCard(newNum); } + public void removeCard(BonusCard card){ + if(bonusCardControlMap.containsKey(card)){ + bonusCardIntegerMap.put(card, bonusCardIntegerMap.get(card) - 1); + + if(bonusCardIntegerMap.get(card) <= 0){ + cardLayer.deleteSpatial(bonusCardControlMap.get(card).getRoot()); + bonusCardIntegerMap.remove(card); + bonusCardControlMap.remove(card); + } + + } + } + public void clearSelectableCards() { for (CardControl control : selectableCards) { control.setSelectable(false); diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java index d4128081..c4024fbe 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java @@ -74,8 +74,31 @@ public void hideDice() { cardLayerHandler.hideDice(); } - public void addCard(BonusCard card) { + //add own handCard + public void addCardOwn(BonusCard card) { cardLayerHandler.addCard(card); + playerNameHandler.addCard(ownColor); + actionTextHandler.drawCardOwn(ownColor); + } + + public void playCardOwn(BonusCard card){ + getEffectByCard(card); + cardLayerHandler.removeCard(card); + playerNameHandler.removeCard(ownColor); + } + + public void playCardEnemy(Color color, BonusCard card) { + getEffectByCard(card); + playerNameHandler.removeCard(color); + } + + private void getEffectByCard(BonusCard bonus){ + switch(bonus){ + case SWAP -> swap(); + case TURBO -> turbo(); + case SHIELD -> shield(); + default -> throw new RuntimeException("invalid card"); + } } public void clearSelectableCards() { @@ -125,9 +148,11 @@ public void hideText(){ actionTextHandler.hide(); } + //addCard Enemy (DrawCardNotification) public void drawCard(Color color) { - if (ownColor == color) actionTextHandler.drawCardOwn(color); - else actionTextHandler.drawCard(playerNameHandler.getName(color), color); + //Color != ownColor + actionTextHandler.drawCard(playerNameHandler.getName(color), color); + playerNameHandler.addCard(color); } public void finish(Color color){ @@ -141,4 +166,6 @@ public void rollRankingResult(Color color, int eye){ } + + } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java index 2d4bdbf7..e5069eae 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java @@ -4,23 +4,27 @@ import com.jme3.font.BitmapFont; import com.jme3.font.BitmapText; import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.system.AppSettings; import com.jme3.ui.Picture; +import pp.mdga.game.BonusCard; import pp.mdga.game.Color; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.Vector; public class PlayerNameHandler { private final BitmapFont playerFont; private final Node playerNameNode; private final List playerOrder; private final Map colorNameMap; + private final Map colorCardMap; + private final AppSettings appSettings; private final AssetManager assetManager; private Color ownColor; @@ -43,6 +47,7 @@ public PlayerNameHandler(Node guiNode, AssetManager assetManager, AppSettings ap playerNameNode = new Node("player name node"); playerOrder = new ArrayList<>(); colorNameMap = new HashMap<>(); + colorCardMap = new HashMap<>(); this.appSettings = appSettings; this.assetManager = assetManager; } @@ -64,13 +69,38 @@ private void drawPlayers(){ if(!colorNameMap.containsKey(color)) throw new RuntimeException(color + " isn't mapped to a name"); Node nameParent = new Node("nameParent"); - nameParent.attachChild(createName(colorNameMap.get(color), i == 0, color == ownColor)); nameParent.attachChild(createColor(color)); + BitmapText name = createName(colorNameMap.get(color), i == 0, color == ownColor); + nameParent.attachChild(name); + if(colorCardMap.getOrDefault(color, 0) > 0){ + Picture pic = createHandCard(name.getLineWidth()); + nameParent.attachChild(pic); + nameParent.attachChild(createCardNum(colorCardMap.get(color), pic.getWidth(), pic.getLocalTranslation().getX())); + } nameParent.setLocalTranslation(50,appSettings.getWindowHeight()-PADDING_TOP- MARGIN_NAMES *i,0); playerNameNode.attachChild(nameParent); } } + private Spatial createCardNum(int num, float lastWidth, float lastX ) { + BitmapText hudText = new BitmapText(playerFont); + //renderedSize = 45 + hudText.setSize(TEXT_SIZE); + hudText.setColor(NORMAL_COLOR); + hudText.setText(String.valueOf(num)); + hudText.setLocalTranslation(lastX + lastWidth + 20,hudText.getHeight()/2, 0); + return hudText; + } + + private Picture createHandCard(float width) { + Picture pic = new Picture("HUD Picture"); + pic.setImage(assetManager, "./Images/handcard.png", true); + pic.setWidth(IMAGE_SIZE); + pic.setHeight(IMAGE_SIZE); + pic.setPosition(-pic.getWidth()/2 + width + PADDING_LEFT * 2 ,-pic.getHeight()/2); + return pic; + } + private String imagePath(Color color){ String root = "./Images/name_pictures/"; return switch(color){ @@ -93,7 +123,7 @@ private Spatial createColor(Color color) { - private Spatial createName(String name, boolean first, boolean own){ + private BitmapText createName(String name, boolean first, boolean own){ BitmapText hudText = new BitmapText(playerFont); //renderedSize = 45 hudText.setSize(TEXT_SIZE); @@ -124,5 +154,18 @@ public String getName(Color color){ return colorNameMap.get(color); } + public void addCard(Color color){ + colorCardMap.put(color, colorCardMap.getOrDefault(color, 0) + 1); + drawPlayers(); + } + + public void removeCard(Color color){ + if(colorCardMap.containsKey(color)){ + colorCardMap.put(color, colorCardMap.getOrDefault(color, 0) - 1); + if(colorCardMap.get(color) <= 0) colorCardMap.remove(color); + } + drawPlayers(); + } + } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java index f28719ac..60a2be45 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java @@ -65,10 +65,11 @@ public void onEnter() { app.getAcousticHandler().playSound(MdgaSound.START); -// guiHandler.addPlayer(Color.AIRFORCE, "Cedric"); -// guiHandler.addPlayer(Color.ARMY, "Ben"); -// guiHandler.addPlayer(Color.CYBER, "Felix"); -// guiHandler.addPlayer(Color.NAVY, "Daniel"); + guiHandler.addPlayer(Color.AIRFORCE, "Cedric"); + guiHandler.addPlayer(Color.ARMY, "Ben"); + guiHandler.addPlayer(Color.CYBER, "Felix"); + guiHandler.addPlayer(Color.NAVY, "Daniel"); + } diff --git a/Projekte/mdga/client/src/main/resources/Images/handcard.png b/Projekte/mdga/client/src/main/resources/Images/handcard.png new file mode 100644 index 0000000000000000000000000000000000000000..6a30c799ee29d7d9bc10844b013d4dbbc7a56939 GIT binary patch literal 12257 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 Date: Wed, 4 Dec 2024 13:33:39 +0100 Subject: [PATCH 82/82] Try to make server stop on leave --- .../src/main/java/pp/mdga/client/MdgaApp.java | 7 +++ .../mdga/client/NotificationSynchronizer.java | 3 ++ .../pp/mdga/client/dialog/JoinDialog.java | 12 ++++++ .../pp/mdga/client/dialog/NetworkDialog.java | 43 +++++++++++++++---- .../java/pp/mdga/client/view/MainView.java | 8 ++++ 5 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index b0120dfb..b92d8768 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -291,5 +291,12 @@ public static void restartApp() { throw new RuntimeException("restart failed"); } } + + public void afterGameCleanup() { + MainView main = (MainView) mainView; + + main.getJoinDialog().disconnect(); + main.getHostDialog().shutdownServer(); + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 0ffe9d44..b8347058 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -71,6 +71,7 @@ private void handleLobby(Notification notification) { if (notification instanceof TskSelectNotification n) { lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName()); } else if (notification instanceof StartDialogNotification) { + app.afterGameCleanup(); app.enter(MdgaState.MAIN); } else if (notification instanceof TskUnselectNotification n) { lobbyView.setTaken(n.getColor(), false, false, null); @@ -174,6 +175,7 @@ private void handleGame(Notification notification) { } else if (notification instanceof ShieldSuppressedNotification n) { boardHandler.suppressShield(n.getPieceId()); } else if (notification instanceof StartDialogNotification) { + app.afterGameCleanup(); app.enter(MdgaState.MAIN); } else if (notification instanceof SwapPieceNotification n) { // boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece()); @@ -200,6 +202,7 @@ private void handleGame(Notification notification) { private void handleCeremony(Notification notification) { if (notification instanceof StartDialogNotification) { + app.afterGameCleanup(); app.enter(MdgaState.MAIN); } else { throw new RuntimeException("notification not expected in ceremony: " + notification.getClass().getName()); diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java index 067ba3c7..eedd8cb8 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java @@ -92,4 +92,16 @@ public void resetPort() { public void connectToServer() { connectServer(); } + + public void disconnect() { + NetworkSupport network = getNetwork(); + if (network != null) { + try { + network.disconnect(); + } catch (Exception e) { + System.err.println("Error while disconnecting: " + e.getMessage()); + } + } + } } + diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java index 15c680e9..d44de294 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/NetworkDialog.java @@ -14,6 +14,8 @@ public abstract class NetworkDialog extends Dialog { private String hostname; private int portNumber; private Future connectionFuture; + private MdgaServer serverInstance; + private Thread serverThread; public NetworkDialog(MdgaApp app, Node node, NetworkSupport network) { super(app, node); @@ -28,7 +30,6 @@ public void setPortNumber(int portNumber) { this.portNumber = portNumber; } - protected Object initNetwork() { try { this.network.initNetwork(this.hostname, this.portNumber); @@ -39,25 +40,49 @@ protected Object initNetwork() { } protected void connectServer() { - try { connectionFuture = this.network.getApp().getExecutor().submit(this::initNetwork); } catch (NumberFormatException var2) { throw new NumberFormatException("Port must be a number"); } - } protected void startServer() { - (new Thread(() -> { + serverThread = new Thread(() -> { try { - MdgaServer mdgaServer = new MdgaServer(portNumber); - mdgaServer.run(); + serverInstance = new MdgaServer(portNumber); + serverInstance.run(); } catch (Exception e) { throw new RuntimeException(e); } + }); - })).start(); + serverThread.start(); + } + + public void shutdownServer() { + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.err.println("Thread was interrupted: " + e.getMessage()); + } + + if (serverInstance != null) { + serverInstance.shutdown(); + serverInstance = null; + } + + if (serverThread != null && serverThread.isAlive()) { + serverThread.interrupt(); + try { + serverThread.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + serverThread = null; + } } public void update(float delta) { @@ -65,14 +90,14 @@ public void update(float delta) { try { this.connectionFuture.get(); } catch (ExecutionException ignored) { - // todo: implement + // TODO: implement } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } - public NetworkSupport getNetwork(){ + public NetworkSupport getNetwork() { return network; } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java index e19fd274..d5653f6d 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/MainView.java @@ -231,5 +231,13 @@ public void back() { break; } } + + public JoinDialog getJoinDialog() { + return joinDialog; + } + + public HostDialog getHostDialog() { + return hostDialog; + } }