Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer bugs fixing #219

Merged
merged 62 commits into from
Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
391f82f
frontEnd for multiplayer
uo288061 Apr 9, 2024
152e216
init backEnd for multiplayer
uo288061 Apr 9, 2024
3eb53d9
more backEnd for multiplayer
uo288061 Apr 9, 2024
06f209d
fixing backEnd for multiplayer (It is not yet implemented) done about…
uo288061 Apr 10, 2024
15c35d5
starting multiplayer lobby
uo288061 Apr 10, 2024
7c197e8
done multiplayer lobby, now the list of players updates automatically
uo288061 Apr 10, 2024
989fee5
fix some problems
uo288061 Apr 10, 2024
c1a4d4e
create and join buttons working correctly
uo288061 Apr 11, 2024
acd0e36
players can can play multiplayer games
uo288061 Apr 11, 2024
c46e273
The multiplayer mode is done but some bugs need to be fixed
uo288061 Apr 13, 2024
81accd1
Players with same multiplayerCode, have same questions
uo288061 Apr 14, 2024
4968265
Update multiplayerGame.html style
RicardoDiNu Apr 15, 2024
863dc8f
Update lobby.html style
RicardoDiNu Apr 15, 2024
db833af
Fix small bug of code validation (only numbers are permited)
RicardoDiNu Apr 15, 2024
586805e
Change multiFinished.html style
RicardoDiNu Apr 15, 2024
b32fa20
FinishGame bug fixed
RicardoDiNu Apr 16, 2024
33111ed
Duplicate multiFinished window bug fixed
RicardoDiNu Apr 16, 2024
db70848
multiFinished style update
RicardoDiNu Apr 16, 2024
b4b0d1d
Some endMultiGame logic added
RicardoDiNu Apr 16, 2024
01543f7
new entity on the dataBase (MultiplayerSession)
uo288061 Apr 23, 2024
f9ea290
Error fixed - new entity on the dataBase (MultiplayerSession)
uo288061 Apr 23, 2024
59e24bc
more changes
uo288061 Apr 23, 2024
fcb1a15
logic for MultiplayerSession done
uo288061 Apr 23, 2024
2773302
some problems fixed
uo288061 Apr 23, 2024
cdca207
frontend for multiplayerRanking.html done
uo288061 Apr 23, 2024
c1793ba
some important changes for multiplayer logic
uo288061 Apr 23, 2024
e603718
Remove old commented code
Pelayori Apr 24, 2024
2b58429
all problems fixed (I think)
uo288061 Apr 24, 2024
147bb7d
small changes
uo288061 Apr 24, 2024
0c7930b
Add table and dropdown to user management
Pelayori Apr 24, 2024
c85ee32
All multiplayerGame correct
uo288061 Apr 25, 2024
af999eb
cleaning code
uo288061 Apr 25, 2024
0a8e957
Merge remote-tracking branch 'origin/develop' into multiplayer-bugs-f…
uo288061 Apr 25, 2024
f92a7b1
problems solved
uo288061 Apr 25, 2024
ee0ae97
Finish user management
Pelayori Apr 26, 2024
afa0bbe
Start adding question management
Pelayori Apr 26, 2024
8c1a4ae
Merge branch 'add-question-templates' into admin-view
Pelayori Apr 26, 2024
2776571
Question management mostly done
Pelayori Apr 26, 2024
4ea0663
unit tests for MultiplayerSession
uo288061 Apr 26, 2024
ccc1a72
unit tests for MultiplayerSession (fixed)
uo288061 Apr 26, 2024
89a6352
Modify so question generation starts over with new JSON
Pelayori Apr 26, 2024
da799e9
Fix sticky footer
Pelayori Apr 26, 2024
ddf24c3
Fix code hotspot
Pelayori Apr 26, 2024
78cde1b
Fix nav
Pelayori Apr 26, 2024
6dbbd28
Fix nav in small widths
Pelayori Apr 26, 2024
3725378
User management integration tests
Pelayori Apr 26, 2024
6b8b6dc
Question management integration tests
Pelayori Apr 26, 2024
eec984f
Merge pull request #210 from Arquisoft/add-question-templates
Pelayori Apr 26, 2024
35ecb0f
Merge branch 'develop' into admin-view
Pelayori Apr 26, 2024
8d6c06e
Merge pull request #211 from Arquisoft/admin-view
Pelayori Apr 26, 2024
88ccb95
Fix sonarcloud issues
Pelayori Apr 26, 2024
704843a
MultiplayerSession Unit Tests - Entities
RicardoDiNu Apr 26, 2024
ba731ef
MultiplayerSession Unit Tests - GameService
RicardoDiNu Apr 26, 2024
e0f6fd7
MultiplayerSession Unit Tests - QuestionService
RicardoDiNu Apr 26, 2024
dfd89ec
MultiplayerSession Unit Tests - PlayerService(users)
RicardoDiNu Apr 26, 2024
05e25ee
MultiplayerSession Unit Tests - PlayerService(score)
RicardoDiNu Apr 26, 2024
183e050
MultiplayerSession Unit Tests - PlayerService(session and code)
RicardoDiNu Apr 26, 2024
49b1911
Merge branch 'develop' into multiplayer-bugs-fixing
Pelayori Apr 26, 2024
418b15e
Merge and fix footers
Pelayori Apr 26, 2024
b3bceb7
Fix tests
Pelayori Apr 26, 2024
98464b7
Fix tests
Pelayori Apr 26, 2024
929a7b2
Fix tests
Pelayori Apr 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

@Component
public interface QuestionGenerator {
List<Question> getQuestions(String language) throws IOException;
List<Question> getQuestions(String language) throws IOException, InterruptedException;

List<Question> getQuestions(String language, JsonNode question, Category cat) throws IOException;
List<Question> getQuestions(String language, JsonNode question, Category cat) throws IOException, InterruptedException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class QuestionGeneratorV2 implements QuestionGenerator{

Expand All @@ -27,6 +29,8 @@ public class QuestionGeneratorV2 implements QuestionGenerator{
private String answer_placeholder;
private String language;

private Random random = new SecureRandom();

public QuestionGeneratorV2(JsonNode jsonNode) {
this.jsonNode = jsonNode;
this.language_placeholder = jsonNode.get("language_placeholder").textValue();
Expand All @@ -35,7 +39,7 @@ public QuestionGeneratorV2(JsonNode jsonNode) {
}

@Override
public List<Question> getQuestions(String language) throws IOException {
public List<Question> getQuestions(String language) throws IOException, InterruptedException {
this.language = language;
List<Question> questions = new ArrayList<>();
JsonNode categories = jsonNode.findValue("categories");
Expand All @@ -51,12 +55,12 @@ public List<Question> getQuestions(String language) throws IOException {
}

@Override
public List<Question> getQuestions(String language, JsonNode question, Category cat) throws IOException {
public List<Question> getQuestions(String language, JsonNode question, Category cat) throws IOException, InterruptedException {
this.language = language;
return this.generateQuestion(question, cat);
}

private List<Question> generateQuestion(JsonNode question, Category cat) throws IOException {
private List<Question> generateQuestion(JsonNode question, Category cat) throws IOException, InterruptedException {
// Get the SPARQL query from the JSON
String query = question.get("sparqlQuery").textValue();

Expand Down Expand Up @@ -85,14 +89,16 @@ private List<Question> generateQuestion(JsonNode question, Category cat) throws
List<Answer> options = this.generateOptions(results, correctAnswer, answerLabel);
options.add(correct);

// Generate the question statement
String questionStatement = statement.replace(question_placeholder, result.path(questionLabel).path("value").asText());
if (statement != null) {
// Generate the question statement
String questionStatement = statement.replace(question_placeholder, result.path(questionLabel).path("value").asText());

// Generate the question
Question q = new Question(questionStatement, options, correct, cat, language);
// Generate the question
Question q = new Question(questionStatement, options, correct, cat, language);

// Add the question to the list
questions.add(q);
// Add the question to the list
questions.add(q);
}
}
return questions;
}
Expand All @@ -103,8 +109,8 @@ private List<Answer> generateOptions(JsonNode results, String correctAnswer, Str
int size = results.size();
int tries = 0;

while (options.size() < 3 && tries < 10){
int random = (int) (Math.random() * size);
while (options.size() < 3 && tries < 10) {
int random = (int) (this.random.nextFloat() * size);
String option = results.get(random).path(answerLabel).path("value").asText();
if (!option.equals(correctAnswer) && !usedOptions.contains(option) ) {
usedOptions.add(option);
Expand All @@ -130,41 +136,28 @@ private String prepareStatement(JsonNode question) {
return null;
}

private JsonNode getQueryResult(String query) throws IOException {
private JsonNode getQueryResult(String query) throws IOException, InterruptedException {

System.out.println("Query: " + query);
HttpClient client = HttpClient.newHttpClient();
JsonNode resultsNode;
try {

String endpointUrl = "https://query.wikidata.org/sparql?query=" +
URLEncoder.encode(query, StandardCharsets.UTF_8) +
"&format=json";
String endpointUrl = "https://query.wikidata.org/sparql?query=" +
URLEncoder.encode(query, StandardCharsets.UTF_8) +
"&format=json";

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpointUrl))
.header("Accept", "application/json")
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpointUrl))
.header("Accept", "application/json")
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// Process the JSON response using Jackson ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonResponse = objectMapper.readTree(response.body());
// Process the JSON response using Jackson ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonResponse = objectMapper.readTree(response.body());

// Access the data from the JSON response
resultsNode = jsonResponse.path("results").path("bindings");

} catch (InterruptedException e) {
throw new QuestionGeneratorException("Generation of questions was interrupted");
}
// Access the data from the JSON response
resultsNode = jsonResponse.path("results").path("bindings");
return resultsNode;

}

private static class QuestionGeneratorException extends RuntimeException {
public QuestionGeneratorException(String message) {
super(message);
}
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/uniovi/configuration/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ 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()
).formLogin(
form -> form
Expand Down
146 changes: 131 additions & 15 deletions src/main/java/com/uniovi/controllers/GameController.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,19 +20,24 @@
import java.security.Principal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.*;

@Controller
public class GameController {
private final QuestionService questionService;
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;
}


Expand All @@ -46,6 +55,60 @@ public String getGame(HttpSession session, Model model, Principal principal) {
}
} else {
gameSession = gameSessionService.startNewGame(getLoggedInPlayer(principal));
playerService.deleteMultiplayerCode(gameSession.getPlayer().getId());
session.setAttribute("gameSession", gameSession);

}

model.addAttribute("question", gameSession.getCurrentQuestion());
model.addAttribute("questionDuration", getRemainingTime(gameSession));
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> 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> 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);
}

Expand All @@ -54,6 +117,53 @@ public String getGame(HttpSession session, Model model, Principal principal) {
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<String, String> endMultiplayerGameTable(@PathVariable String code) {
Map<Player, Integer> playerScores = multiplayerSessionService.getPlayersWithScores(Integer.parseInt(code));
Map<String, String> playersNameWithScore=new HashMap<>();
for (Map.Entry<Player, Integer> 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<String> updatePlayerList(@PathVariable String code) {
Map<Player,Integer> players= multiplayerSessionService.getPlayersWithScores(Integer.parseInt(code));
List<String> playerNames = new ArrayList<>();
for (Map.Entry<Player, Integer> 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<Player> 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
Expand All @@ -65,7 +175,7 @@ public String getGame(HttpSession session, Model model, Principal principal) {
* shown or the timeOutFailure view is shown.
*/
@GetMapping("/game/{idQuestion}/{idAnswer}")
public String getCheckResult(@PathVariable Long idQuestion, @PathVariable Long idAnswer, Model model, HttpSession session) {
public String getCheckResult(@PathVariable Long idQuestion, @PathVariable Long idAnswer, Model model, HttpSession session,Principal principal) {
GameSession gameSession = (GameSession) session.getAttribute("gameSession");
if (gameSession == null) {
return "redirect:/game";
Expand All @@ -79,39 +189,45 @@ public String getCheckResult(@PathVariable Long idQuestion, @PathVariable Long i

if(idAnswer == -1
|| getRemainingTime(gameSession) <= 0) {
//model.addAttribute("correctAnswer", gameSession.getCurrentQuestion().getCorrectAnswer());
//model.addAttribute("messageKey", "timeRunOut.result");
//model.addAttribute("logoImage", "/images/logo_incorrect.svg");
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
gameSession.addQuestion(false, 0);
}
else if(questionService.checkAnswer(idQuestion, idAnswer)) {
//model.addAttribute("messageKey", "correctAnswer.result");
//model.addAttribute("logoImage", "/images/logo_correct.svg");

if (!gameSession.isAnswered(gameSession.getCurrentQuestion())) {
gameSession.addQuestion(true, getRemainingTime(gameSession));
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
}

} else {
//model.addAttribute("correctAnswer", gameSession.getCurrentQuestion().getCorrectAnswer());
//model.addAttribute("messageKey", "failedAnswer.result");
//model.addAttribute("logoImage", "/images/logo_incorrect.svg");
gameSession.addAnsweredQuestion(gameSession.getCurrentQuestion());
gameSession.addQuestion(false, 0);
}

session.setAttribute("hasJustAnswered", true);
gameSession.getNextQuestion();
//return "game/fragments/questionResult";
return updateGame(model, session);
return updateGame(model, session, principal);
}

@GetMapping("/game/update")
public String updateGame(Model model, HttpSession session) {
public String updateGame(Model model, HttpSession session, Principal principal) {
GameSession gameSession = (GameSession) session.getAttribute("gameSession");
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<Player> players=playerService.getUsersByMultiplayerCode(code);
model.addAttribute("players",players);
model.addAttribute("code",session.getAttribute("multiplayerCode"));
session.removeAttribute("gameSession");

Optional<Player> 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");
Expand Down
Loading
Loading