diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java index 8a80810..6267756 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java @@ -42,6 +42,9 @@ public class Game implements Serializable { @GeneratedValue(strategy = GenerationType.IDENTITY) private BigDecimal id; + @Transient + private int currentRound = 0; + @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_at") private Date createdAt; @@ -78,6 +81,9 @@ public class Game implements Serializable { @Transient public static final int NB_ROUNDS_MIN = 3; + @Transient + private GameState gameState = GameState.WAITING; + /** * Constructeur par défaut */ @@ -275,6 +281,10 @@ public class Game implements Serializable { } public Deck getDeck() { + if (deck == null) { + deck = new Deck(nbColors, nbValuesPerColor); + deck.shuffle(); + } return deck; } @@ -339,5 +349,26 @@ public class Game implements Serializable { * Difficulté possible d'une partie */ public enum Difficulty {EASY, HARD} + + public enum GameState {WAITING, STARTED, FINISHED} + + public GameState getGameState() { + return gameState; + } + + public void setGameState(GameState gameState) { + this.gameState = gameState; + } + + public int getCurrentRound() { + return currentRound; + } + + public boolean nextRound() { + currentRound++; + + if (currentRound > nbRounds) gameState = GameState.FINISHED; + return (currentRound > nbRounds); + } } diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Player.java b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Player.java index c9602bc..b90db50 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Player.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Player.java @@ -15,9 +15,11 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; +import jakarta.websocket.Session; import java.io.Serializable; import java.math.BigDecimal; import java.util.Objects; +import uppa.project.json.websocket.ClickChoice; /** * Représentation d'un joueur @@ -60,12 +62,31 @@ public class Player implements Serializable { @Transient private Deck deck; + @Transient + private Session session = null; + + @Transient + private ClickChoice currentClick = null; + /** * Constructeur par défaut */ public Player() { } + public Player(Game game, User user, Session session) { + this.game = game; + this.user = user; + this.session = session; + this.score = 0; + this.winner = false; + this.clickCount = 0; + this.rightClickCount = 0; + this.rapidClickCount = 0; + this.deck = new Deck(game.getNbColors(), game.getNbValuesPerColor()); + this.deck.shuffle(); + } + /** * Constructeur d'un joueur * @@ -295,4 +316,16 @@ public class Player implements Serializable { public BigDecimal getId() { return id; } + + public Session getSession() { + return session; + } + + public ClickChoice getCurrentClick() { + return currentClick; + } + + public void setCurrentClick(ClickChoice currentClick) { + this.currentClick = currentClick; + } } diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java new file mode 100644 index 0000000..35b0aeb --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java @@ -0,0 +1,8 @@ +package uppa.project.json.websocket; + +public enum ClickChoice { + COLOR_VALUE, + COLOR, + VALUE, + NONE +} diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java new file mode 100644 index 0000000..8821e81 --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java @@ -0,0 +1,32 @@ +package uppa.project.json.websocket; + +import java.util.ArrayList; +import uppa.project.database.pojo.Card; +import uppa.project.database.pojo.Game; +import uppa.project.database.pojo.Player; + +public class SimpleGame { + + private final int id; + private final ArrayList players; + private final Card currentCard; + + public SimpleGame(Game game, ArrayList playerArrayList) { + this.id = game.getId().intValue(); + this.players = new ArrayList<>(); + for (Player p : playerArrayList) players.add(new SimplePlayer(p, game.getCurrentRound())); + this.currentCard = game.getDeck().getCards().get(game.getCurrentRound()); + } + + public int getId() { + return id; + } + + public ArrayList getPlayers() { + return players; + } + + public Card getCurrentCard() { + return currentCard; + } +} diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleInvitation.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleInvitation.java new file mode 100644 index 0000000..fe18322 --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleInvitation.java @@ -0,0 +1,28 @@ +package uppa.project.json.websocket; + +public class SimpleInvitation { + + private final SimpleUser from; + + private final SimpleUser to; + + private final int game_id; + + public SimpleInvitation(SimpleUser from, SimpleUser to, int game_id) { + this.from = from; + this.to = to; + this.game_id = game_id; + } + + public SimpleUser getFrom() { + return from; + } + + public SimpleUser getTo() { + return to; + } + + public int getGameId() { + return game_id; + } +} \ No newline at end of file diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java new file mode 100644 index 0000000..88cb454 --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java @@ -0,0 +1,64 @@ +package uppa.project.json.websocket; + +import uppa.project.database.pojo.Card; +import uppa.project.database.pojo.Player; + +public class SimplePlayer { + + private final SimpleUser user; + private final int score; + private final boolean winner; + private final int clickCount; + private final int rightClickCount; + private final int rapidClickCount; + private final Card currentCard; + + public SimplePlayer(Player player, int currentRound) { + this.user = new SimpleUser(player.getUser()); + this.score = player.getScore(); + this.winner = player.isWinner(); + this.clickCount = player.getClickCount(); + this.rightClickCount = player.getRightClickCount(); + this.rapidClickCount = player.getRapidClickCount(); + + this.currentCard = player.getDeck().getCards().get(currentRound); + } + + public SimplePlayer(Player player) { + this.user = new SimpleUser(player.getUser()); + this.score = player.getScore(); + this.winner = player.isWinner(); + this.clickCount = player.getClickCount(); + this.rightClickCount = player.getRightClickCount(); + this.rapidClickCount = player.getRapidClickCount(); + this.currentCard = null; + } + + public Card getCurrentCard() { + return currentCard; + } + + public SimpleUser getUser() { + return user; + } + + public int getScore() { + return score; + } + + public boolean isWinner() { + return winner; + } + + public int getClickCount() { + return clickCount; + } + + public int getRightClickCount() { + return rightClickCount; + } + + public int getRapidClickCount() { + return rapidClickCount; + } +} diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleUser.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleUser.java new file mode 100644 index 0000000..74b5686 --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleUser.java @@ -0,0 +1,21 @@ +package uppa.project.json.websocket; + +import uppa.project.database.pojo.User; + +public class SimpleUser { + private final int id; + private final String username; + + public SimpleUser(User user) { + this.id = user.getId().intValue(); + this.username = user.getUsername(); + } + + public int getId() { + return id; + } + + public String getUsername() { + return username; + } +} \ No newline at end of file diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/utils/GameProvider.java b/S2/DevWeb/Projet/src/main/java/uppa/project/utils/GameProvider.java new file mode 100644 index 0000000..05e6068 --- /dev/null +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/utils/GameProvider.java @@ -0,0 +1,34 @@ +package uppa.project.utils; + +import java.util.HashMap; +import uppa.project.database.dao.DAO; +import uppa.project.database.dao.DAOException; +import uppa.project.database.dao.jpa.DAO_JPA_Game; +import uppa.project.database.pojo.Game; + +public class GameProvider { + + private static DAO gameDAO; + + static { + try { + gameDAO = new DAO_JPA_Game(); + } catch (DAOException e) { + throw new RuntimeException(e); + } + } + + private static HashMap games = new HashMap<>(); + + public static Game getGame(int gameId) throws DAOException { + if (!games.containsKey(gameId)) { + Game game = gameDAO.findById(gameId); + games.put(gameId, game); + } + + if (games.get(gameId) == null) { + throw new DAOException("Game not found"); + } + return games.get(gameId); + } +} diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/ConnectedUsersWS.java b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/ConnectedUsersWS.java index d961ec8..ff12995 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/ConnectedUsersWS.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/ConnectedUsersWS.java @@ -16,6 +16,8 @@ import uppa.project.database.dao.DAOException; import uppa.project.database.dao.jpa.Game_JPA_DAO_Factory; import uppa.project.database.pojo.User; import uppa.project.json.websocket.Message; +import uppa.project.json.websocket.SimpleInvitation; +import uppa.project.json.websocket.SimpleUser; @ServerEndpoint(value = "/ws/users/{user_id}") public class ConnectedUsersWS { @@ -99,7 +101,7 @@ public class ConnectedUsersWS { // Find session of the user who receive for (Session s : users.keySet()) { - if (users.get(s).getId().intValue() == invitation.to.id) { + if (users.get(s).getId().intValue() == invitation.getTo().getId()) { try { s.getBasicRemote().sendText(message.toJson()); } catch (IOException e) { @@ -110,29 +112,4 @@ public class ConnectedUsersWS { } } } - - private static class SimpleInvitation { - - public SimpleUser from; - - public SimpleUser to; - - public int game_id; - - public SimpleInvitation(SimpleUser from, SimpleUser to, int game_id) { - this.from = from; - this.to = to; - this.game_id = game_id; - } - } - - private static class SimpleUser { - public int id; - public String username; - - public SimpleUser(User user) { - this.id = user.getId().intValue(); - this.username = user.getUsername(); - } - } } diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java index c21fddd..0102c9a 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java @@ -1,5 +1,7 @@ package uppa.project.web.websocket; +import com.google.gson.Gson; +import jakarta.persistence.EntityManager; import jakarta.websocket.OnClose; import jakarta.websocket.OnError; import jakarta.websocket.OnMessage; @@ -9,24 +11,46 @@ import jakarta.websocket.server.PathParam; import jakarta.websocket.server.ServerEndpoint; import java.util.ArrayList; import java.util.HashMap; +import uppa.project.database.dao.DAO; import uppa.project.database.dao.DAOException; +import uppa.project.database.dao.EntityManagerProvider; +import uppa.project.database.dao.jpa.DAO_JPA_User; +import uppa.project.database.pojo.Card; import uppa.project.database.pojo.Game; +import uppa.project.database.pojo.Player; import uppa.project.database.pojo.User; +import uppa.project.json.websocket.ClickChoice; +import uppa.project.json.websocket.Message; +import uppa.project.json.websocket.SimpleGame; +import uppa.project.json.websocket.SimplePlayer; +import uppa.project.json.websocket.SimpleUser; +import uppa.project.utils.GameProvider; -@ServerEndpoint(value = "/ws/game/{game_id}/{user_id}") +@ServerEndpoint(value = "/ws/game/{game_id}") public class GameWS { - public static final HashMap users = new HashMap<>(); - public static final HashMap> games = new HashMap<>(); + Gson gson = new Gson(); + + private static final HashMap> games = new HashMap<>(); + + private Game game; + private Player player; @OnOpen - public void onOpen(Session session, @PathParam("game_id") String gameId, @PathParam("user_id") String userId) throws DAOException { - + public void onOpen(Session session, @PathParam("game_id") String gameId) throws DAOException { + this.game = GameProvider.getGame(Integer.parseInt(gameId)); + if (!games.containsKey(game)) games.put(game, new ArrayList<>()); } @OnClose - public void onClose(Session session, @PathParam("game_id") String gameId, @PathParam("user_id") String userId) { + public void onClose(Session session, @PathParam("game_id") String gameId) throws DAOException { + games.get(game).remove(this.player); + // Broadcast the new player + ArrayList simplePlayerList = new ArrayList<>(); + for (Player player : games.get(game)) simplePlayerList.add(new SimplePlayer(player)); + + broadcast(new Message("updatePlayerList", gson.toJson(simplePlayerList)).toJson()); } @OnError @@ -35,7 +59,137 @@ public class GameWS { } @OnMessage - public void onMessage(String message, Session session) { - // Do nothing + public void onMessage(String rawMessage, Session session, @PathParam("game_id") String gameId) throws DAOException { + DAO userDAO = new DAO_JPA_User(); + + Message message = gson.fromJson(rawMessage, Message.class); + + if (message.getType().equals("connection")) { + SimpleUser simpleUser = gson.fromJson(message.getData(), SimpleUser.class); + + // find the user + User user = userDAO.findById(simpleUser.getId()); + + this.player = new Player(game, user, session); + games.get(game).add(this.player); + + ArrayList simplePlayerList = new ArrayList<>(); + for (Player p : games.get(game)) simplePlayerList.add(new SimplePlayer(p)); + + // Broadcast the new player + broadcast(new Message("updatePlayerList", gson.toJson(simplePlayerList)).toJson()); + } + if (message.getType().equals("start")) { + game.setGameState(Game.GameState.STARTED); + broadcast(new Message("start", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); + // TODO Start Timer + } + if (message.getType().equals("click")) { + ClickChoice choice = gson.fromJson(message.getData(), ClickChoice.class); + + int playerScore = player.getScore(); + Card gameCard = game.getDeck().getCards().get(game.getCurrentRound()); + Card playerCard = player.getDeck().getCards().get(game.getCurrentRound()); + + player.setCurrentClick(choice); + + // get the number of players who clicked + int gameClickCount = 0; + for (Player player : games.get(game)) { + if (player.getCurrentClick() != null) gameClickCount++; + } + + // Click count + player.incrementClickCount(); + + // Right click count + if (gameClickCount == 1) player.incrementRapidClickCount(); + + // Check if the player has clicked on the right card + switch (choice) { + case COLOR_VALUE -> { + if (gameCard.getColor().equals(playerCard.getColor()) && gameCard.getValue() == playerCard.getValue()) { + player.incrementRightClickCount(); + player.setScore(playerScore + 2); + } else { + player.setScore(playerScore - 1); + } + } + case COLOR -> { + if (gameCard.getColor().equals(playerCard.getColor())) { + if (gameCard.getValue() != playerCard.getValue()) { + player.incrementRightClickCount(); + player.setScore(playerScore + 2); + } else { + player.setScore(playerScore + 1); + } + } else { + player.setScore(playerScore - 1); + } + } + case VALUE -> { + if(gameCard.getValue() == playerCard.getValue()) { + if (!gameCard.getColor().equals(playerCard.getColor())) { + player.incrementRightClickCount(); + player.setScore(playerScore + 2); + } else { + player.setScore(playerScore + 1); + } + } else { + player.setScore(playerScore - 1); + } + } + case NONE -> { + if (!gameCard.getColor().equals(playerCard.getColor()) && gameCard.getValue() != playerCard.getValue()) { + player.incrementRightClickCount(); + player.setScore(playerScore + 2); + } else { + player.setScore(playerScore - 1); + } + } + } + + // Broadcast the player choice with score + + broadcast(new Message("updatePlayer", gson.toJson(new SimplePlayer(player))).toJson()); + + System.out.println(gameClickCount + " / " + games.get(game).size()); + + // If all players have clicked + if (gameClickCount >= games.get(game).size()) { + + // Reset the current click for all players + for (Player p : games.get(game)) p.setCurrentClick(null); + + // Check if the game is over + if (game.nextRound()) { // TODO: if score is the same add a round + broadcast(new Message("nextRound", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); + // TODO Start Timer + } else { + // TODO: determine the winner + + // Broadcast the end of the game + broadcast(new Message("end", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); + + // TODO: persist the game in the database +// EntityManager em = EntityManagerProvider.getInstance(); +// game.setPlayers(games.get(game)); +// +// em.getTransaction().begin(); +// em.persist(game); +// em.getTransaction().commit(); + } + } + } + } + + private void broadcast(String message) { + for (Player player : games.get(game)) { + try { + player.getSession().getBasicRemote().sendText(message); + } catch (Exception e) { + e.printStackTrace(); + } + } } } diff --git a/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp b/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp index f03f79a..275c9ba 100644 --- a/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp +++ b/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp @@ -5,12 +5,22 @@ -
+ +
Ajouter + Démarrer + + + + + + +
Joueur
+
@@ -25,6 +35,23 @@
+ @@ -154,4 +181,159 @@ ws.onClose(() => console.log("Disconnected from the server")); + + \ No newline at end of file