diff --git a/.github/workflows/unit-tests-push.yml b/.github/workflows/unit-tests-push.yml index 17e4cfb8..ab827dbe 100644 --- a/.github/workflows/unit-tests-push.yml +++ b/.github/workflows/unit-tests-push.yml @@ -74,4 +74,4 @@ jobs: kill $(cat spring-boot-app.pid) - name: Collect Jacoco report and send to Sonar run: | - ./mvnw org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dsonar.projectKey=Arquisoft_wiq_es04b -Dsonar.organization=arquisoft -Dsonar.branch.name=${{ github.ref }} -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${{ secrets.SONAR_TOKEN }} -Dspring.profiles.active=test \ No newline at end of file + ./mvnw org.jacoco:jacoco-maven-plugin:report sonar:sonar -Dsonar.projectKey=Arquisoft_wiq_es04b -Dsonar.organization=arquisoft -Dsonar.branch.name=${{ github.head_ref || github.ref_name }} -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${{ secrets.SONAR_TOKEN }} -Dspring.profiles.active=test \ No newline at end of file diff --git a/src/main/java/com/uniovi/configuration/SecurityConfig.java b/src/main/java/com/uniovi/configuration/SecurityConfig.java index 6f1c0087..14586d1b 100644 --- a/src/main/java/com/uniovi/configuration/SecurityConfig.java +++ b/src/main/java/com/uniovi/configuration/SecurityConfig.java @@ -47,6 +47,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/signup/**").permitAll() .requestMatchers("/api/**").permitAll() .requestMatchers("/game/**").authenticated() + .requestMatchers("/multiplayerGame/**").authenticated() + .requestMatchers("/lobby/**").authenticated() .requestMatchers("/ranking/playerRanking").authenticated() .requestMatchers("/player/admin/**").hasAuthority("ROLE_ADMIN") .requestMatchers("/**").permitAll() diff --git a/src/main/java/com/uniovi/controllers/GameController.java b/src/main/java/com/uniovi/controllers/GameController.java index 5154df38..e54fd52f 100644 --- a/src/main/java/com/uniovi/controllers/GameController.java +++ b/src/main/java/com/uniovi/controllers/GameController.java @@ -1,12 +1,16 @@ package com.uniovi.controllers; import com.uniovi.entities.GameSession; +import com.uniovi.entities.MultiplayerSession; import com.uniovi.entities.Player; import com.uniovi.entities.Question; import com.uniovi.services.GameSessionService; +import com.uniovi.services.MultiplayerSessionService; import com.uniovi.services.PlayerService; import com.uniovi.services.QuestionService; import jakarta.servlet.http.HttpSession; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -16,7 +20,7 @@ import java.security.Principal; import java.time.Duration; import java.time.LocalDateTime; -import java.util.Optional; +import java.util.*; @Controller public class GameController { @@ -26,11 +30,16 @@ public class GameController { private final GameSessionService gameSessionService; private final PlayerService playerService; + private final MultiplayerSessionService multiplayerSessionService; + + private boolean isMultiPlayer; + public GameController(QuestionService questionService, GameSessionService gameSessionService, - PlayerService playerService) { + PlayerService playerService, MultiplayerSessionService multiplayerSessionService) { this.questionService = questionService; this.gameSessionService = gameSessionService; this.playerService = playerService; + this.multiplayerSessionService = multiplayerSessionService; } /** @@ -48,6 +57,9 @@ public String getGame(HttpSession session, Model model, Principal principal) { } else { gameSession = gameSessionService.startNewGame(getLoggedInPlayer(principal)); session.setAttribute(GAMESESSION_STR, gameSession); + playerService.deleteMultiplayerCode(gameSession.getPlayer().getId()); + session.setAttribute("gameSession", gameSession); + } model.addAttribute("question", gameSession.getCurrentQuestion()); @@ -55,6 +67,105 @@ public String getGame(HttpSession session, Model model, Principal principal) { return "game/basicGame"; } + @GetMapping("/multiplayerGame") + public String getMultiplayerGame() { + return "game/multiplayerGame"; + } + + @GetMapping("/multiplayerGame/{code}") + public String joinMultiplayerGame(@PathVariable String code, HttpSession session, Principal principal, Model model) { + Optional player = playerService.getUserByUsername(principal.getName()); + Player p = player.orElse(null); + isMultiPlayer=true; + if(playerService.changeMultiplayerCode(p.getId(),code)){ + multiplayerSessionService.addToLobby(code,p.getId()); + model.addAttribute("multiplayerGameCode",code); + session.setAttribute("multiplayerCode",code); + return "redirect:/game/lobby"; + } else { + return "redirect:/multiplayerGame"; + } + } + + @GetMapping("/multiplayerGame/createGame") + public String createMultiplayerGame(HttpSession session, Principal principal, Model model) { + Optional player = playerService.getUserByUsername(principal.getName()); + Player p = player.orElse(null); + String code=""+playerService.createMultiplayerGame(p.getId()); + multiplayerSessionService.multiCreate(code,p.getId()); + session.setAttribute("multiplayerCode",code); + isMultiPlayer=true; + return "redirect:/game/lobby"; + } + + @GetMapping("/startMultiplayerGame") + public String startMultiplayerGame(HttpSession session, Model model, Principal principal) { + GameSession gameSession = (GameSession) session.getAttribute("gameSession"); + if(! isMultiPlayer){ + return "index"; + } + if (gameSession != null) { + if (checkUpdateGameSession(gameSession, session)) { + return "game/fragments/gameFinished"; + } + } else { + gameSession = gameSessionService.startNewMultiplayerGame(getLoggedInPlayer(principal), + playerService.getUserByUsername(principal.getName()).get().getMultiplayerCode()); + session.setAttribute("gameSession", gameSession); + } + + model.addAttribute("question", gameSession.getCurrentQuestion()); + model.addAttribute("questionDuration", getRemainingTime(gameSession)); + return "game/basicGame"; + } + + @GetMapping("/multiplayerGame/endGame/{code}") + public String endMultiplayerGame(Model model,@PathVariable String code) { + model.addAttribute("code",code); + return "ranking/multiplayerRanking"; + } + @GetMapping("/endGameList/{code}") + @ResponseBody + public Map endMultiplayerGameTable(@PathVariable String code) { + Map playerScores = multiplayerSessionService.getPlayersWithScores(Integer.parseInt(code)); + Map playersNameWithScore=new HashMap<>(); + for (Map.Entry player : playerScores.entrySet()) { + String playerName = player.getKey().getUsername(); + String playerScoreValue; + if(player.getValue()==-1){ + playerScoreValue="N/A"; + }else{ + playerScoreValue=""+player.getValue(); + } + playersNameWithScore.put(playerName, playerScoreValue); + } + return playersNameWithScore; + } + + @GetMapping("/game/lobby/{code}") + @ResponseBody + public List updatePlayerList(@PathVariable String code) { + Map players= multiplayerSessionService.getPlayersWithScores(Integer.parseInt(code)); + List playerNames = new ArrayList<>(); + for (Map.Entry player : players.entrySet()) { + playerNames.add(player.getKey().getUsername()); + } + Collections.sort(playerNames); + return playerNames; + } + @GetMapping("/game/lobby") + public String createLobby( HttpSession session, Model model) { + int code = Integer.parseInt((String)session.getAttribute("multiplayerCode")); + List players=playerService.getUsersByMultiplayerCode(code); + model.addAttribute("players",players); + model.addAttribute("code",session.getAttribute("multiplayerCode")); + return "/game/lobby"; + } + + @GetMapping("/game/startMultiplayerGame") + public String startMultiplayerGame( HttpSession session, Model model) { + return "/game/lobby"; + } /** * This method is used to check the answer for a specific question @@ -96,14 +207,29 @@ else if(questionService.checkAnswer(idQuestion, idAnswer)) { session.setAttribute("hasJustAnswered", true); gameSession.getNextQuestion(); - - return updateGame(model, session); + return updateGame(model, session, principal); } @GetMapping("/game/update") public String updateGame(Model model, HttpSession session) { GameSession gameSession = (GameSession) session.getAttribute(GAMESESSION_STR); Question nextQuestion = gameSession.getCurrentQuestion(); + if(nextQuestion == null && isMultiPlayer/*gameSession.getPlayer().getMultiplayerCode()!=null session.getAttribute("multiplayerCode") !=null*/){ + gameSessionService.endGame(gameSession); + + int code = Integer.parseInt((String)session.getAttribute("multiplayerCode")); + List players=playerService.getUsersByMultiplayerCode(code); + model.addAttribute("players",players); + model.addAttribute("code",session.getAttribute("multiplayerCode")); + session.removeAttribute("gameSession"); + + Optional player = playerService.getUserByUsername(principal.getName()); + Player p = player.orElse(null); + playerService.setScoreMultiplayerCode(p.getId(),""+gameSession.getScore()); + multiplayerSessionService.changeScore(p.getMultiplayerCode()+"",p.getId(),gameSession.getScore()); + isMultiPlayer=false; + return "game/multiFinished"; + } if (nextQuestion == null) { gameSessionService.endGame(gameSession); session.removeAttribute(GAMESESSION_STR); diff --git a/src/main/java/com/uniovi/dto/PlayerDto.java b/src/main/java/com/uniovi/dto/PlayerDto.java index 027b26fa..2e3d6571 100644 --- a/src/main/java/com/uniovi/dto/PlayerDto.java +++ b/src/main/java/com/uniovi/dto/PlayerDto.java @@ -22,6 +22,9 @@ public class PlayerDto { @Schema(hidden = true) private String passwordConfirm; + //@Schema(description = "code of group of the player", example = "5565") + //private Integer multiplayerCode; + @Schema(description = "Roles of the player", example = "[\"ROLE_USER\"]") private String[] roles; } diff --git a/src/main/java/com/uniovi/entities/MultiplayerSession.java b/src/main/java/com/uniovi/entities/MultiplayerSession.java new file mode 100644 index 00000000..e9b77a5c --- /dev/null +++ b/src/main/java/com/uniovi/entities/MultiplayerSession.java @@ -0,0 +1,38 @@ +package com.uniovi.entities; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Getter // getters para todas las propiedades +@Setter // setters para todas las propiedades +@Entity +public class MultiplayerSession { + @Id + @GeneratedValue + private Long id; + @Column + private String multiplayerCode; + + @ElementCollection + @Column + private Map playerScores = new HashMap<>(); + + public MultiplayerSession() {} + + public MultiplayerSession(String code, Player p) { + this.multiplayerCode=code; + playerScores.put(p,-1); + + } + + public void addPlayer(Player p){ + playerScores.put(p,-1); + } +} diff --git a/src/main/java/com/uniovi/entities/Player.java b/src/main/java/com/uniovi/entities/Player.java index 491a8bd8..97b93fd2 100644 --- a/src/main/java/com/uniovi/entities/Player.java +++ b/src/main/java/com/uniovi/entities/Player.java @@ -33,6 +33,13 @@ public class Player implements JsonEntity { @NotEmpty private String password; + @Column + private Integer multiplayerCode; + + @Column + private String scoreMultiplayerCode; + + @ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER) private Set roles = new HashSet<>(); @@ -42,6 +49,8 @@ public class Player implements JsonEntity { @OneToOne(cascade = CascadeType.ALL, mappedBy = "player") private ApiKey apiKey; + + // Transient: no se almacena en la base de datos @Transient private String passwordConfirm; diff --git a/src/main/java/com/uniovi/repositories/MultiplayerSessionRepository.java b/src/main/java/com/uniovi/repositories/MultiplayerSessionRepository.java new file mode 100644 index 00000000..fd9e926e --- /dev/null +++ b/src/main/java/com/uniovi/repositories/MultiplayerSessionRepository.java @@ -0,0 +1,17 @@ +package com.uniovi.repositories; + +import com.uniovi.entities.GameSession; +import com.uniovi.entities.MultiplayerSession; +import com.uniovi.entities.Player; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface MultiplayerSessionRepository extends CrudRepository { + MultiplayerSession findByMultiplayerCode(String code); +} + diff --git a/src/main/java/com/uniovi/repositories/PlayerRepository.java b/src/main/java/com/uniovi/repositories/PlayerRepository.java index cf4f0a98..254adb52 100644 --- a/src/main/java/com/uniovi/repositories/PlayerRepository.java +++ b/src/main/java/com/uniovi/repositories/PlayerRepository.java @@ -1,6 +1,7 @@ package com.uniovi.repositories; import com.uniovi.entities.Player; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.CrudRepository; @@ -8,6 +9,8 @@ public interface PlayerRepository extends CrudRepository { Player findByEmail(String email); Player findByUsername(String nickname); + @Query("SELECT player FROM Player player WHERE player.multiplayerCode=:multiplayerCode") + Iterable findAllByMultiplayerCode(int multiplayerCode); Page findAll(Pageable pageable); } diff --git a/src/main/java/com/uniovi/services/GameSessionService.java b/src/main/java/com/uniovi/services/GameSessionService.java index f1872836..d3ee5f67 100644 --- a/src/main/java/com/uniovi/services/GameSessionService.java +++ b/src/main/java/com/uniovi/services/GameSessionService.java @@ -40,5 +40,8 @@ public interface GameSessionService { Page getPlayerRanking(Pageable pageable, Player player); GameSession startNewGame(Player player); + + GameSession startNewMultiplayerGame(Player player, int code); + void endGame(GameSession gameSession); } diff --git a/src/main/java/com/uniovi/services/MultiplayerSessionService.java b/src/main/java/com/uniovi/services/MultiplayerSessionService.java new file mode 100644 index 00000000..c0932339 --- /dev/null +++ b/src/main/java/com/uniovi/services/MultiplayerSessionService.java @@ -0,0 +1,22 @@ +package com.uniovi.services; + +import com.uniovi.entities.GameSession; +import com.uniovi.entities.MultiplayerSession; +import com.uniovi.entities.Player; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +public interface MultiplayerSessionService { + + Map getPlayersWithScores(int multiplayerCode); + void multiCreate(String code, Long id); + + void addToLobby(String code, Long id); + + void changeScore(String code,Long id,int score); +} diff --git a/src/main/java/com/uniovi/services/PlayerService.java b/src/main/java/com/uniovi/services/PlayerService.java index c241de5b..e8a19a29 100644 --- a/src/main/java/com/uniovi/services/PlayerService.java +++ b/src/main/java/com/uniovi/services/PlayerService.java @@ -25,6 +25,13 @@ public interface PlayerService { */ List getUsers(); + + /** + * Get all the players in the database with same multiplayerCode + * @return A list with the players + */ + List getUsersByMultiplayerCode(int multiplayerCode); + /** * Get a player by its id * @param id The id of the player @@ -66,6 +73,21 @@ public interface PlayerService { */ void updatePlayer(Long id, PlayerDto playerDto); + /** + * Update the multiplayerCode of a player + * @param id The id of the player to update + * @param code The new multiplayerCode of the player + */ + boolean changeMultiplayerCode(Long id, String code); + + String getScoreMultiplayerCode(Long id); + + void setScoreMultiplayerCode(Long id, String score); + + int createMultiplayerGame(Long id); + + void deleteMultiplayerCode(Long id); + /** * Delete a player from the database * @param id The id of the player to delete diff --git a/src/main/java/com/uniovi/services/QuestionService.java b/src/main/java/com/uniovi/services/QuestionService.java index 1cea8683..924f2c44 100644 --- a/src/main/java/com/uniovi/services/QuestionService.java +++ b/src/main/java/com/uniovi/services/QuestionService.java @@ -59,6 +59,8 @@ public interface QuestionService { */ List getRandomQuestions(int num); + List getRandomMultiplayerQuestions(int num, int code); + /** * Check if the answer is correct * @param idquestion The id of the question diff --git a/src/main/java/com/uniovi/services/impl/GameSessionImpl.java b/src/main/java/com/uniovi/services/impl/GameSessionImpl.java index 97a4ab87..aca3509e 100644 --- a/src/main/java/com/uniovi/services/impl/GameSessionImpl.java +++ b/src/main/java/com/uniovi/services/impl/GameSessionImpl.java @@ -50,6 +50,11 @@ public GameSession startNewGame(Player player) { return new GameSession(player, questionService.getRandomQuestions(NORMAL_GAME_QUESTION_NUM)); } + @Override + public GameSession startNewMultiplayerGame(Player player, int code) { + return new GameSession(player, questionService.getRandomMultiplayerQuestions(NORMAL_GAME_QUESTION_NUM,code)); + } + @Override public void endGame(GameSession gameSession) { Associations.PlayerGameSession.addGameSession(gameSession.getPlayer(), gameSession); diff --git a/src/main/java/com/uniovi/services/impl/MultiplayerSessionImpl.java b/src/main/java/com/uniovi/services/impl/MultiplayerSessionImpl.java new file mode 100644 index 00000000..0c10044e --- /dev/null +++ b/src/main/java/com/uniovi/services/impl/MultiplayerSessionImpl.java @@ -0,0 +1,69 @@ +package com.uniovi.services.impl; + +import com.uniovi.entities.MultiplayerSession; +import com.uniovi.entities.Player; +import com.uniovi.repositories.MultiplayerSessionRepository; +import com.uniovi.repositories.PlayerRepository; +import com.uniovi.services.MultiplayerSessionService; +import jakarta.transaction.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class MultiplayerSessionImpl implements MultiplayerSessionService { + private final PlayerRepository playerRepository; + private final MultiplayerSessionRepository multiplayerSessionRepository; + + + public MultiplayerSessionImpl(PlayerRepository playerRepository, MultiplayerSessionRepository multiplayerSessionRepository) { + this.playerRepository = playerRepository; + this.multiplayerSessionRepository = multiplayerSessionRepository; + } + + @Override + @Transactional + public Map getPlayersWithScores(int multiplayerCode) { + MultiplayerSession session = multiplayerSessionRepository.findByMultiplayerCode(String.valueOf(multiplayerCode)); + Map playerScores = session.getPlayerScores(); + + // Ordenar los jugadores por puntuación de mayor a menor + List sortedPlayers = playerScores.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + Map playersSorted = new HashMap<>(); + for (Player player : sortedPlayers) { + playersSorted.put(player,playerScores.get(player)); + } + return playersSorted; + } + + @Override + public void multiCreate(String code, Long id) { + Player p = playerRepository.findById(id).get(); + multiplayerSessionRepository.save(new MultiplayerSession(code,p)); + } + + @Override + @Transactional + public void addToLobby(String code, Long id) { + Player p = playerRepository.findById(id).get(); + MultiplayerSession ms=multiplayerSessionRepository.findByMultiplayerCode(code); + ms.addPlayer(p); + multiplayerSessionRepository.save(ms); + } + + @Override + @Transactional + public void changeScore(String code, Long id, int score) { + Player p = playerRepository.findById(id).get(); + MultiplayerSession ms=multiplayerSessionRepository.findByMultiplayerCode(code); + ms.getPlayerScores().put(p,score); + multiplayerSessionRepository.save(ms); + } +} diff --git a/src/main/java/com/uniovi/services/impl/PlayerServiceImpl.java b/src/main/java/com/uniovi/services/impl/PlayerServiceImpl.java index 64805509..f8cde7ee 100644 --- a/src/main/java/com/uniovi/services/impl/PlayerServiceImpl.java +++ b/src/main/java/com/uniovi/services/impl/PlayerServiceImpl.java @@ -6,6 +6,7 @@ import com.uniovi.entities.Associations; import com.uniovi.entities.Player; import com.uniovi.repositories.PlayerRepository; +import com.uniovi.services.MultiplayerSessionService; import com.uniovi.services.PlayerService; import com.uniovi.services.RoleService; import org.springframework.data.domain.Page; @@ -24,10 +25,13 @@ public class PlayerServiceImpl implements PlayerService { private final RoleService roleService; private final PasswordEncoder passwordEncoder; - public PlayerServiceImpl(PlayerRepository playerRepository, RoleService roleService, PasswordEncoder passwordEncoder) { + private MultiplayerSessionService multiplayerSessionService; + + public PlayerServiceImpl(PlayerRepository playerRepository, RoleService roleService, MultiplayerSessionService multiplayerSessionService,PasswordEncoder passwordEncoder) { this.playerRepository = playerRepository; this.roleService = roleService; this.passwordEncoder = passwordEncoder; + this.multiplayerSessionService = multiplayerSessionService; } @Override @@ -70,6 +74,13 @@ public List getUsers() { return l; } + @Override + public List getUsersByMultiplayerCode(int multiplayerCode) { + List l = new ArrayList<>(); + playerRepository.findAllByMultiplayerCode(multiplayerCode).forEach(l::add); + return l; + } + @Override public Optional getUser(Long id) { return playerRepository.findById(id); @@ -130,6 +141,72 @@ public void updatePlayer(Long id, PlayerDto playerDto) { playerRepository.save(p); } + @Override + public boolean changeMultiplayerCode(Long id, String code) { + Optional player = playerRepository.findById(id); + if (player.isEmpty()) + return false; + + Player p = player.get(); + if(existsMultiplayerCode(code)){ + p.setMultiplayerCode(Integer.parseInt(code)); + playerRepository.save(p); + return true; + } + return false; + } + @Override + public String getScoreMultiplayerCode(Long id) { + Optional player = playerRepository.findById(id); + if (player.isEmpty()) + return ""; + + return player.get().getScoreMultiplayerCode(); + } + + @Override + public void setScoreMultiplayerCode(Long id, String score) { + Optional player = playerRepository.findById(id); + if (player.isEmpty()) + return; + + Player p =player.get(); + p.setScoreMultiplayerCode(score); + playerRepository.save(p); + } + /** + * A multiplayerCodeExists if there are any player + * with same multiplayerCode at the moment of the join + * */ + private boolean existsMultiplayerCode(String code){ + //return ! getUsersByMultiplayerCode(Integer.parseInt(code)).isEmpty(); + return ! multiplayerSessionService.getPlayersWithScores(Integer.parseInt(code)).isEmpty(); + } + + @Override + public int createMultiplayerGame(Long id){ + Optional player = playerRepository.findById(id); + if (player.isEmpty()) + return -1; + + Player p = player.get(); + int code = (int)Math.floor(Math.random()*10000); + p.setMultiplayerCode(code); + playerRepository.save(p); + return code; + } + + @Override + public void deleteMultiplayerCode(Long id){ + Optional player = playerRepository.findById(id); + if (player.isEmpty()) + return; + + Player p = player.get(); + p.setMultiplayerCode(null); + playerRepository.save(p); + } + @Override public void deletePlayer(Long id) { playerRepository.deleteById(id); diff --git a/src/main/java/com/uniovi/services/impl/QuestionServiceImpl.java b/src/main/java/com/uniovi/services/impl/QuestionServiceImpl.java index ffe3bd3a..c5c8a656 100644 --- a/src/main/java/com/uniovi/services/impl/QuestionServiceImpl.java +++ b/src/main/java/com/uniovi/services/impl/QuestionServiceImpl.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.security.SecureRandom; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -111,6 +113,39 @@ public List getRandomQuestions(int num) { return res; } + + @Override + public List getRandomMultiplayerQuestions(int num, int code) { + List allQuestions = questionRepository.findAll().stream() + .filter(question -> question.getLanguage().equals(LocaleContextHolder.getLocale().getLanguage())).toList(); + List res = new ArrayList<>(); + int size= allQuestions.size(); + + int currentIndex = generateIndex(code, size) -4; + + for (int i = 0; i < num; i++) { + + Question question = allQuestions.get(currentIndex); + + while (question.hasEmptyOptions() || res.contains(question)) { + question = allQuestions.get(currentIndex); + } + + res.add(question); + currentIndex++; + } + return res; + } + private int generateIndex(int code, int size) { + int hashCode = combineHash(code, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + return Math.abs(hashCode) % size; + } + + private int combineHash(int code, String date) { + String combinedString = code + date; + return combinedString.hashCode(); + } + @Override public boolean checkAnswer(Long idquestion, Long idanswer) { Optional q = questionRepository.findById(idquestion); @@ -176,4 +211,5 @@ public void deleteAllQuestions() throws IOException { questionGeneratorService.resetGeneration(); questionRepository.deleteAll(); } + } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index e2304c4b..ea14131d 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -39,6 +39,7 @@ error.error=Error: index.heading=WIQ index.subtitle=Responde las preguntas correctamente y GANA!!! index.button=JUGAR +index.multiplayer.button=JUGAR CON AMIGOS # -------------------Statements for the home.html file--------------------- home.heading=Bienvenido @@ -84,6 +85,29 @@ ranking.question.right=Respuestas correctas ranking.question.wrong=Respuestas incorrectas ranking.time=Tiempo +# -------------------Statements for the multiplayerGame.html file--------------------- +multi.text = ¿Aún no tienes un código? Crea uno y compártelo con tus amigos +multi.create = Crear +multi.placeholder= Introduce el código correcto +multi.label = Únete a una partida +multi.join = Unirse +multi.onlyNumber = Solo se permiten números +multi.copyCode= Copiar código +multi.info=Resultados para la partida: + +# -------------------Statements for the lobby.html file--------------------- +lobby.info =Jugadores unidos a la partida: +lobby.friends =Comparte el código de tu partida con tus amigos +lobby.start = Empezar + +# -------------------Statements for the lobby.html file--------------------- +multi.code= Código de la partida +multi.results =Ver resultados + +# -------------------Statements for the multiFinished.html file--------------------- +multi.finished= Partida finalizada +multi.points = Puntuaciones +multi.menu = Ir a la página de inicio # -------------------Statements for the apiHome.html file--------------------- api.doc.title=Documentación de la API api.doc.description=Esta es la documentación de la API de WIQ. Aquí puedes encontrar información sobre los recursos disponibles, los parámetros que aceptan y los ejemplos de uso. diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 331afdb3..479c891f 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -39,6 +39,8 @@ error.error=Error: index.heading=WIQ index.subtitle=Answer the questions correctly and WIN!!! index.button=PLAY +index.multiplayer.button=PLAY WITH FRIENDS + # -------------------Statements for the home.html file--------------------- home.heading=Welcome @@ -85,6 +87,31 @@ ranking.question.right=Right answers ranking.question.wrong=Wrong answers ranking.time=Time +# -------------------Statements for the multiplayerGame.html file--------------------- +multi.text =Don't have a code yet? Create one and share it with your friends +multi.create = Create +multi.placeholder=Enter the correct code +multi.label=Join a game +multi.join = Join +multi.onlyNumber=Only numbers allowed +multi.copyCode= Copy code +multi.info=reults for the game: + +# -------------------Statements for the lobby.html file--------------------- +lobby.info =Players joining the game: +lobby.friends =Share your game code with your friends +lobby.start =Start + +# -------------------Statements for the lobby.html file--------------------- +multi.code= Game code +multi.results = See results + +# -------------------Statements for the multiFinished.html file--------------------- +multi.finished= Finished game +multi.points = Points +multi.menu =Go to home page + + # -------------------Statements for the apiHome.html file--------------------- api.doc.title=API Documentation api.doc.description=This document describes the REST API endpoints. diff --git a/src/main/resources/messages_es.properties b/src/main/resources/messages_es.properties index f4e31807..3a860636 100644 --- a/src/main/resources/messages_es.properties +++ b/src/main/resources/messages_es.properties @@ -39,6 +39,8 @@ error.error=Error: index.heading=WIQ index.subtitle=Responde las preguntas correctamente y GANA!!! index.button=JUGAR +index.multiplayer.button=JUGAR CON AMIGOS + # -------------------Statements for the home.html file--------------------- home.heading=Bienvenido @@ -85,6 +87,31 @@ ranking.question.right=Respuestas correctas ranking.question.wrong=Respuestas incorrectas ranking.time=Tiempo +# -------------------Statements for the multiplayerGame.html file--------------------- +multi.text = ¿Aún no tienes un código? Crea uno y compártelo con tus amigos +multi.create = Crear +multi.placeholder= Introduce el código correcto +multi.label = Únete a una partida +multi.join = Unirse +multi.onlyNumber = Solo se permiten números +multi.copyCode= Copiar código +multi.info=Resultados para la partida: + +# -------------------Statements for the lobby.html file--------------------- +lobby.info =Jugadores unidos a la partida: +lobby.friends =Comparte el código de tu partida con tus amigos +lobby.start =Empezar + +# -------------------Statements for the lobby.html file--------------------- +multi.code= Código de la partida +multi.results =Ver resultados + +# -------------------Statements for the multiFinished.html file--------------------- +multi.finished= Partida finalizada +multi.points = Puntuaciones +multi.menu = Ir a la página de inicio + + # -------------------Statements for the apiHome.html file--------------------- api.doc.title=Documentación de la API api.doc.description=Esta es la documentación de la API de WIQ. Aquí puedes encontrar información sobre los recursos disponibles, los parámetros que aceptan y los ejemplos de uso. diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties index 7f233db5..96a377f1 100644 --- a/src/main/resources/messages_fr.properties +++ b/src/main/resources/messages_fr.properties @@ -37,6 +37,7 @@ error.error=Erreur : index.heading=WIQ index.subtitle=Répondez aux questions correctement et GAGNEZ !!! index.button=JOUER +index.multiplayer.button=JOUEZ AVEC DES AMIS # -------------------Statements for the home.html file--------------------- home.heading=Bienvenue @@ -79,6 +80,31 @@ ranking.question.right=Réponses correctes ranking.question.wrong=Réponses incorrectes ranking.time=Temps +# -------------------Statements for the multiplayerGame.html file--------------------- +multi.text = Vous n'avez pas encore de code ? Créez-en un et partagez-le avec vos amis. +multi.create = Créer +multi.placeholder=Entrez le bon code +multi.label=Rejoignez une partie +multi.join =Rejoindre +multi.onlyNumber=Seuls les chiffres sont autorisés +multi.copyCode= Copier le code +multi.info=résultats du jeu: + +# -------------------Statements for the lobby.html file--------------------- +lobby.info =Joueurs rejoignant le jeu : +lobby.friends =Partagez votre code de jeu avec vos amis +lobby.start = Commencer + +# -------------------Statements for the lobby.html file--------------------- +multi.code=Code du jeu +multi.results=Voir les résultats + +# -------------------Statements for the multiFinished.html file--------------------- +multi.finished= Jeu terminé +multi.points = Points +multi.menu =aller à la page d'accueil + + # -------------------Statements for the apiHome.html file--------------------- api.doc.title=Documentation de l'API api.doc.description=Ceci est la documentation de l'API de WIQ. Vous pouvez trouver ici des informations sur les ressources disponibles, les paramètres qu'elles acceptent et des exemples d'utilisation. diff --git a/src/main/resources/static/css/multiplayer.css b/src/main/resources/static/css/multiplayer.css new file mode 100644 index 00000000..6715e189 --- /dev/null +++ b/src/main/resources/static/css/multiplayer.css @@ -0,0 +1,26 @@ +.display-5 { + margin-top: 3em; +} + +#lobbyCode, #label-code { + font-size: 3em; +} + +#lobbyInfo { + margin-top: 3em; +} + +#playerList { + list-style-type: none; + text-align: center; + padding: 0; +} + +#finishedGame { + font-size: 5em; + margin-bottom: 0.5em; +} + +#multiPoints { + margin-top: 3em; +} diff --git a/src/main/resources/templates/game/lobby.html b/src/main/resources/templates/game/lobby.html new file mode 100644 index 00000000..116de0f7 --- /dev/null +++ b/src/main/resources/templates/game/lobby.html @@ -0,0 +1,59 @@ + + + + + + + + + +
+

+ + + +

+
    +
  • +
+ + + +
+ +
+ + diff --git a/src/main/resources/templates/game/multiFinished.html b/src/main/resources/templates/game/multiFinished.html new file mode 100644 index 00000000..3aff9e76 --- /dev/null +++ b/src/main/resources/templates/game/multiFinished.html @@ -0,0 +1,19 @@ + +
+ +

+ +

+ + + + + + + +
\ No newline at end of file diff --git a/src/main/resources/templates/game/multiplayerGame.html b/src/main/resources/templates/game/multiplayerGame.html new file mode 100644 index 00000000..6b26fe71 --- /dev/null +++ b/src/main/resources/templates/game/multiplayerGame.html @@ -0,0 +1,60 @@ + + + + + + + + +
+ +
+ + + +
+ + +

+ +

+ + + +
+ + +
+ + + + + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 15ef5f21..3116c7e7 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -10,6 +10,8 @@

+

+ @@ -18,6 +20,11 @@

window.location.href = "/game"; }); +
diff --git a/src/main/resources/templates/ranking/multiplayerRanking.html b/src/main/resources/templates/ranking/multiplayerRanking.html new file mode 100644 index 00000000..4da4cbf9 --- /dev/null +++ b/src/main/resources/templates/ranking/multiplayerRanking.html @@ -0,0 +1,48 @@ + + + + +