From 25c2270a37c2eb8cf7602dc3b5b3fe052dbb0400 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 30 Jan 2025 22:16:29 +0100 Subject: [PATCH] feat: multi synced player scores --- app/src/main/java/common/ConsumerSignal.java | 27 +++++++++ app/src/main/java/game/Player.java | 10 ++++ app/src/main/java/gui/SudokuRenderer.java | 6 +- app/src/main/java/gui/SudokuSelector.java | 12 ++-- .../java/gui/menu/MultiPlayerDokuView.java | 6 ++ .../main/java/gui/menu/MultiPlayerView.java | 4 +- app/src/main/java/gui/menu/SoloMenu.java | 5 +- .../main/java/network/ConnexionThread.java | 2 +- app/src/main/java/network/client/Client.java | 16 ++++++ .../java/network/client/ClientConnexion.java | 22 +++++++ .../java/network/protocol/PacketVisitor.java | 8 ++- .../main/java/network/protocol/Packets.java | 2 +- .../protocol/packets/ChangeCellPacket.java | 38 +++++++++++++ .../protocol/packets/EndGamePacket.java | 26 +++++++++ .../packets/UpdatePlayerScorePacket.java | 32 +++++++++++ app/src/main/java/network/server/Server.java | 3 + .../java/network/server/ServerConnexion.java | 57 ++++++++++++++++++- app/src/main/java/sudoku/structure/Cell.java | 2 + .../main/java/sudoku/structure/MultiDoku.java | 8 ++- 19 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/common/ConsumerSignal.java create mode 100644 app/src/main/java/network/protocol/packets/ChangeCellPacket.java create mode 100644 app/src/main/java/network/protocol/packets/EndGamePacket.java create mode 100644 app/src/main/java/network/protocol/packets/UpdatePlayerScorePacket.java diff --git a/app/src/main/java/common/ConsumerSignal.java b/app/src/main/java/common/ConsumerSignal.java new file mode 100644 index 0000000..ba4b046 --- /dev/null +++ b/app/src/main/java/common/ConsumerSignal.java @@ -0,0 +1,27 @@ +package common; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +public class ConsumerSignal { + private final Set> listeners; + + public ConsumerSignal() { + this.listeners = new HashSet<>(); + } + + public void connect(Consumer listener) { + this.listeners.add(listener); + } + + public void clear() { + this.listeners.clear(); + } + + public void emit(T arg) { + for (Consumer listener : this.listeners) { + listener.accept(arg); + } + } +} diff --git a/app/src/main/java/game/Player.java b/app/src/main/java/game/Player.java index 2336152..9c76a5f 100644 --- a/app/src/main/java/game/Player.java +++ b/app/src/main/java/game/Player.java @@ -8,10 +8,20 @@ public class Player implements Serializable { private final String pseudo; private final int id; + private int score; public Player(int id, String pseudo) { this.pseudo = pseudo; this.id = id; + this.score = 0; + } + + public int getScore() { + return score; + } + + public void setScore(int score) { + this.score = score; } public String getPseudo() { diff --git a/app/src/main/java/gui/SudokuRenderer.java b/app/src/main/java/gui/SudokuRenderer.java index 7b66580..e40e697 100644 --- a/app/src/main/java/gui/SudokuRenderer.java +++ b/app/src/main/java/gui/SudokuRenderer.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import common.ConsumerSignal; import common.Signal; import gui.ColorGenerator.Color; import imgui.ImGui; @@ -33,6 +34,7 @@ public class SudokuRenderer { private final Set diagonals = new HashSet<>(); public final Signal onResolve = new Signal(); + public final ConsumerSignal onCellChange = new ConsumerSignal<>(); public SudokuRenderer(MultiDoku doku) { this.doku = RenderableMultidoku.fromMultidoku(doku); @@ -72,11 +74,13 @@ public class SudokuRenderer { if (currentCell.getSymbolIndex() == i) { if (ImGui.button("X", cellSize)) { currentCell.setSymbolIndex(Cell.NOSYMBOL); + this.onCellChange.emit(currentCell); ImGui.closeCurrentPopup(); } } else { if (ImGui.button(Options.Symboles.getSymbols().get(i), cellSize)) { - currentCell.trySetValue(i); + if (currentCell.trySetValue(i)) + this.onCellChange.emit(currentCell); if (this.doku.getDoku().isSolved()) this.onResolve.emit(); ImGui.closeCurrentPopup(); diff --git a/app/src/main/java/gui/SudokuSelector.java b/app/src/main/java/gui/SudokuSelector.java index 715d7fc..1833f72 100644 --- a/app/src/main/java/gui/SudokuSelector.java +++ b/app/src/main/java/gui/SudokuSelector.java @@ -3,7 +3,7 @@ package gui; import java.util.ArrayList; import java.util.List; -import common.Signal; +import common.ConsumerSignal; import imgui.ImGui; import imgui.extension.imguifiledialog.ImGuiFileDialog; import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags; @@ -16,7 +16,7 @@ import sudoku.structure.SudokuFactory; public class SudokuSelector { - public final Signal onSelect = new Signal(); + public final ConsumerSignal onSelect = new ConsumerSignal<>(); private MultiDoku doku; private final boolean canGenEmptyGrid; @@ -63,7 +63,7 @@ public class SudokuSelector { e.printStackTrace(); } } - this.onSelect.emit(); + this.onSelect.emit(this.doku); } public void renderFileDialog() { @@ -75,7 +75,7 @@ public class SudokuSelector { String filePath = entry.getValue(); this.doku = SudokuFactory.fromfile(filePath); if (this.doku != null) - this.onSelect.emit(); + this.onSelect.emit(this.doku); } catch (Exception e) { e.printStackTrace(); } @@ -129,8 +129,4 @@ public class SudokuSelector { renderFileDialog(); } - public MultiDoku getDoku() { - return doku; - } - } diff --git a/app/src/main/java/gui/menu/MultiPlayerDokuView.java b/app/src/main/java/gui/menu/MultiPlayerDokuView.java index 8974cd0..2721f49 100644 --- a/app/src/main/java/gui/menu/MultiPlayerDokuView.java +++ b/app/src/main/java/gui/menu/MultiPlayerDokuView.java @@ -4,6 +4,7 @@ import gui.SudokuRenderer; import imgui.ImGui; import network.client.Client; import network.server.Server; +import sudoku.structure.Cell; public class MultiPlayerDokuView extends BaseView{ @@ -16,9 +17,14 @@ public class MultiPlayerDokuView extends BaseView{ this.client = client; this.server = server; this.sudokuRenderer = new SudokuRenderer(this.client.getGame().getDoku()); + this.sudokuRenderer.onCellChange.connect(this::onCellChange); this.client.onDisconnect.connect(this::onDisconnect); } + private void onCellChange(Cell cell) { + this.client.sendCellChange(cell); + } + public void onDisconnect() { if (server == null) { closeMenu(); diff --git a/app/src/main/java/gui/menu/MultiPlayerView.java b/app/src/main/java/gui/menu/MultiPlayerView.java index 822dd37..0a373e5 100644 --- a/app/src/main/java/gui/menu/MultiPlayerView.java +++ b/app/src/main/java/gui/menu/MultiPlayerView.java @@ -37,8 +37,8 @@ public class MultiPlayerView extends BaseView { this.stateMachine.popState(); } - private void onSelected() { - this.doku = this.selector.getDoku(); + private void onSelected(MultiDoku doku) { + this.doku = doku; } public void renderGameStatus() { diff --git a/app/src/main/java/gui/menu/SoloMenu.java b/app/src/main/java/gui/menu/SoloMenu.java index d72ef7c..1d50124 100644 --- a/app/src/main/java/gui/menu/SoloMenu.java +++ b/app/src/main/java/gui/menu/SoloMenu.java @@ -2,6 +2,7 @@ package gui.menu; import gui.SudokuSelector; import imgui.ImGui; +import sudoku.structure.MultiDoku; public class SoloMenu extends BaseView { @@ -13,8 +14,8 @@ public class SoloMenu extends BaseView { this.sudokuSelector.onSelect.connect(this::pushSudokuState); } - private void pushSudokuState() { - this.stateMachine.pushState(new SudokuView(stateMachine, this.sudokuSelector.getDoku())); + private void pushSudokuState(MultiDoku doku) { + this.stateMachine.pushState(new SudokuView(stateMachine, doku)); } @Override diff --git a/app/src/main/java/network/ConnexionThread.java b/app/src/main/java/network/ConnexionThread.java index 6752d92..17b0584 100644 --- a/app/src/main/java/network/ConnexionThread.java +++ b/app/src/main/java/network/ConnexionThread.java @@ -22,7 +22,7 @@ public class ConnexionThread extends Thread { // System.out.println(objectInputStream.available()); Object o = objectInputStream.readObject(); if (o instanceof Packet packet) { - connexion.visitPacket(packet); + connexion.visit(packet); } } catch (ClassNotFoundException | IOException e) { e.printStackTrace(); diff --git a/app/src/main/java/network/client/Client.java b/app/src/main/java/network/client/Client.java index 10f5ec8..069b105 100644 --- a/app/src/main/java/network/client/Client.java +++ b/app/src/main/java/network/client/Client.java @@ -7,7 +7,11 @@ import java.util.Random; import common.Signal; import game.Game; import game.Player; +import network.protocol.packets.ChangeCellPacket; import network.protocol.packets.LoginPacket; +import sudoku.structure.Cell; +import sudoku.structure.MultiDoku; +import sudoku.structure.Sudoku; public class Client { private final ClientConnexion clientConnection; @@ -54,4 +58,16 @@ public class Client { stop(); } + public void sendCellChange(Cell cell) { + MultiDoku doku = getGame().getDoku(); + for (int sudokuIndex = 0; sudokuIndex < doku.getNbSubGrids(); sudokuIndex++) { + Sudoku sudoku = doku.getSubGrid(sudokuIndex); + int cellIndex = sudoku.getCells().indexOf(cell); + if (cellIndex != -1) { + this.clientConnection.sendPacket(new ChangeCellPacket(sudokuIndex, cellIndex, cell.getSymbolIndex())); + return; + } + } + } + } diff --git a/app/src/main/java/network/client/ClientConnexion.java b/app/src/main/java/network/client/ClientConnexion.java index 6e6fe87..94e87f9 100644 --- a/app/src/main/java/network/client/ClientConnexion.java +++ b/app/src/main/java/network/client/ClientConnexion.java @@ -6,13 +6,16 @@ import java.net.UnknownHostException; import game.Player; import network.Connexion; +import network.protocol.packets.ChangeCellPacket; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; +import network.protocol.packets.EndGamePacket; import network.protocol.packets.KeepAlivePacket; import network.protocol.packets.LoginPacket; import network.protocol.packets.PlayerJoinPacket; import network.protocol.packets.PlayerLeavePacket; import network.protocol.packets.StartGamePacket; +import network.protocol.packets.UpdatePlayerScorePacket; import sudoku.io.SudokuSerializer; public class ClientConnexion extends Connexion { @@ -73,4 +76,23 @@ public class ClientConnexion extends Connexion { this.client.onGameStarted.emit(); } + @Override + public void visitPacket(EndGamePacket packet) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'visitPacket'"); + } + + @Override + public void visitPacket(UpdatePlayerScorePacket packet) { + Player player = this.client.getGame().getPlayerById(packet.getPlayerId()); + assert(player != null); + player.setScore(packet.getCellsLeft()); + System.out.println("Score for " + player.getPseudo() + " : " + packet.getCellsLeft()); + } + + @Override + public void visitPacket(ChangeCellPacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacketChangeCell'"); + } + } diff --git a/app/src/main/java/network/protocol/PacketVisitor.java b/app/src/main/java/network/protocol/PacketVisitor.java index 582a4f3..9639fd5 100644 --- a/app/src/main/java/network/protocol/PacketVisitor.java +++ b/app/src/main/java/network/protocol/PacketVisitor.java @@ -1,16 +1,19 @@ package network.protocol; +import network.protocol.packets.ChangeCellPacket; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; +import network.protocol.packets.EndGamePacket; import network.protocol.packets.KeepAlivePacket; import network.protocol.packets.LoginPacket; import network.protocol.packets.PlayerJoinPacket; import network.protocol.packets.PlayerLeavePacket; import network.protocol.packets.StartGamePacket; +import network.protocol.packets.UpdatePlayerScorePacket; public interface PacketVisitor { - default void visitPacket(Packet packet) { + default void visit(Packet packet) { packet.accept(this); } @@ -21,5 +24,8 @@ public interface PacketVisitor { void visitPacket(PlayerJoinPacket packet); void visitPacket(PlayerLeavePacket packet); void visitPacket(StartGamePacket packet); + void visitPacket(EndGamePacket packet); + void visitPacket(UpdatePlayerScorePacket packet); + void visitPacket(ChangeCellPacket packet); } diff --git a/app/src/main/java/network/protocol/Packets.java b/app/src/main/java/network/protocol/Packets.java index 9355877..6f056e8 100644 --- a/app/src/main/java/network/protocol/Packets.java +++ b/app/src/main/java/network/protocol/Packets.java @@ -2,6 +2,6 @@ package network.protocol; public enum Packets { - ConnectionInfo, KeepAlive, Disconnect, Login, PlayerJoin, PlayerLeave, StartGame + ConnectionInfo, KeepAlive, Disconnect, Login, PlayerJoin, PlayerLeave, StartGame, ChangeCell, EndGame, UpdatePlayerScore } diff --git a/app/src/main/java/network/protocol/packets/ChangeCellPacket.java b/app/src/main/java/network/protocol/packets/ChangeCellPacket.java new file mode 100644 index 0000000..6d1164e --- /dev/null +++ b/app/src/main/java/network/protocol/packets/ChangeCellPacket.java @@ -0,0 +1,38 @@ +package network.protocol.packets; + +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class ChangeCellPacket extends Packet { + + static private final long serialVersionUID = Packets.ChangeCell.ordinal(); + + private final int sudokuIndex; + private final int cellIndex; + private final int newValue; + + public ChangeCellPacket(int sudokuIndex, int cellIndex, int newValue) { + this.sudokuIndex = sudokuIndex; + this.cellIndex = cellIndex; + this.newValue = newValue; + } + + public int getSudokuIndex() { + return sudokuIndex; + } + + public int getCellIndex() { + return cellIndex; + } + + public int getNewValue() { + return newValue; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + +} diff --git a/app/src/main/java/network/protocol/packets/EndGamePacket.java b/app/src/main/java/network/protocol/packets/EndGamePacket.java new file mode 100644 index 0000000..d01a498 --- /dev/null +++ b/app/src/main/java/network/protocol/packets/EndGamePacket.java @@ -0,0 +1,26 @@ +package network.protocol.packets; + +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class EndGamePacket extends Packet { + + static private final long serialVersionUID = Packets.EndGame.ordinal(); + + private final int winnerId; + + public EndGamePacket(int winnerId) { + this.winnerId = winnerId; + } + + public int getWinnerId() { + return winnerId; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + +} diff --git a/app/src/main/java/network/protocol/packets/UpdatePlayerScorePacket.java b/app/src/main/java/network/protocol/packets/UpdatePlayerScorePacket.java new file mode 100644 index 0000000..f8011df --- /dev/null +++ b/app/src/main/java/network/protocol/packets/UpdatePlayerScorePacket.java @@ -0,0 +1,32 @@ +package network.protocol.packets; + +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class UpdatePlayerScorePacket extends Packet { + + static private final long serialVersionUID = Packets.UpdatePlayerScore.ordinal(); + + private final int playerId; + private final int cellsLeft; + + public UpdatePlayerScorePacket(int playerId, int cellsLeft) { + this.playerId = playerId; + this.cellsLeft = cellsLeft; + } + + public int getPlayerId() { + return playerId; + } + + public int getCellsLeft() { + return cellsLeft; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + +} diff --git a/app/src/main/java/network/server/Server.java b/app/src/main/java/network/server/Server.java index 10ea1f8..79a222d 100644 --- a/app/src/main/java/network/server/Server.java +++ b/app/src/main/java/network/server/Server.java @@ -70,6 +70,9 @@ public class Server { public void startGame(MultiDoku doku) { this.game.startGame(doku); + for (ServerConnexion connexion : this.connexions) { + connexion.setSudoku(doku); + } broadcastPacket(new StartGamePacket(SudokuSerializer.serializeSudoku(doku).toString())); } diff --git a/app/src/main/java/network/server/ServerConnexion.java b/app/src/main/java/network/server/ServerConnexion.java index 8f3307d..d14b2cc 100644 --- a/app/src/main/java/network/server/ServerConnexion.java +++ b/app/src/main/java/network/server/ServerConnexion.java @@ -6,14 +6,19 @@ import java.net.Socket; import game.Player; import game.Game.GameState; import network.Connexion; +import network.protocol.packets.ChangeCellPacket; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; +import network.protocol.packets.EndGamePacket; import network.protocol.packets.KeepAlivePacket; import network.protocol.packets.LoginPacket; import network.protocol.packets.PlayerJoinPacket; import network.protocol.packets.PlayerLeavePacket; import network.protocol.packets.StartGamePacket; +import network.protocol.packets.UpdatePlayerScorePacket; import sudoku.io.SudokuSerializer; +import sudoku.structure.Cell; +import sudoku.structure.MultiDoku; public class ServerConnexion extends Connexion { @@ -21,6 +26,7 @@ public class ServerConnexion extends Connexion { private final KeepAliveHandler keepAliveHandler; private boolean shouldClose = false; private Player player = null; + private MultiDoku doku; public ServerConnexion(Socket socket, Server server) throws IOException { super(socket); @@ -44,7 +50,7 @@ public class ServerConnexion extends Connexion { @Override public synchronized void close() { - if(shouldClose) + if (shouldClose) return; super.close(); shouldClose = true; @@ -54,13 +60,19 @@ public class ServerConnexion extends Connexion { private void finishLogin() { // send players that have already joined (excluding this one) for (Player p : this.server.getGame().getPlayers().values()) { - if (p.getId() != player.getId()) + if (p.getId() != player.getId()) { sendPacket(new PlayerJoinPacket(p)); + sendPacket(new UpdatePlayerScorePacket(p.getId(), p.getScore())); + } } + this.server.broadcastPacket(new PlayerJoinPacket(player)); sendPacket(new ConnexionInfoPacket(player.getId())); + if (this.server.getGame().getGameState() == GameState.GameGoing) { - sendPacket(new StartGamePacket(SudokuSerializer.serializeSudoku(this.server.getGame().getDoku()).toString())); + setSudoku(this.server.getGame().getDoku()); + sendPacket( + new StartGamePacket(SudokuSerializer.serializeSudoku(this.server.getGame().getDoku()).toString())); } } @@ -102,4 +114,43 @@ public class ServerConnexion extends Connexion { throw new UnsupportedOperationException("Unimplemented method 'visitPacketStartGame'"); } + @Override + public void visitPacket(EndGamePacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacket'"); + } + + @Override + public void visitPacket(UpdatePlayerScorePacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacket'"); + } + + @Override + public void visitPacket(ChangeCellPacket packet) { + Cell cell = this.doku.getSubGrid(packet.getSudokuIndex()).getCell(packet.getCellIndex()); + if (cell.getSymbolIndex() == Cell.NOSYMBOL && packet.getNewValue() == Cell.NOSYMBOL) + return; + if (cell.getSymbolIndex() != Cell.NOSYMBOL && packet.getNewValue() != Cell.NOSYMBOL) { + cell.trySetValue(packet.getNewValue()); + return; + } + if (cell.getSymbolIndex() != Cell.NOSYMBOL && packet.getNewValue() == Cell.NOSYMBOL) { + cell.trySetValue(Cell.NOSYMBOL); + player.setScore(player.getScore() + 1); + sendPacket(new UpdatePlayerScorePacket(player.getId(), player.getScore())); + return; + } + // on rajoute un chiffre à la grille + if (cell.trySetValue(packet.getNewValue())) { + player.setScore(player.getScore() - 1); + sendPacket(new UpdatePlayerScorePacket(player.getId(), player.getScore())); + } + } + + public void setSudoku(MultiDoku doku) { + this.doku = doku; + assert (player != null); + player.setScore(this.doku.getEmptyCells().size()); + sendPacket(new UpdatePlayerScorePacket(player.getId(), player.getScore())); + } + } diff --git a/app/src/main/java/sudoku/structure/Cell.java b/app/src/main/java/sudoku/structure/Cell.java index eaa4964..b6127ad 100644 --- a/app/src/main/java/sudoku/structure/Cell.java +++ b/app/src/main/java/sudoku/structure/Cell.java @@ -126,6 +126,8 @@ public class Cell { } public boolean trySetValue(int newValue) { + if (!isMutable()) + return false; if (!canHaveValue(newValue)) return false; setSymbolIndex(newValue); diff --git a/app/src/main/java/sudoku/structure/MultiDoku.java b/app/src/main/java/sudoku/structure/MultiDoku.java index eb64d46..1fd9b6e 100644 --- a/app/src/main/java/sudoku/structure/MultiDoku.java +++ b/app/src/main/java/sudoku/structure/MultiDoku.java @@ -1,8 +1,10 @@ package sudoku.structure; -import java.util.*; - -import sudoku.io.SudokuSerializer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; /** * @class MultiDoku