Skip to content

Commit

Permalink
Pool Game release (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
zelytra authored Jul 2, 2024
1 parent 6d98a32 commit 7f8a290
Show file tree
Hide file tree
Showing 60 changed files with 2,030 additions and 171 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/webapp" # Location of package manifests
schedule:
interval: "weekly"
interval: "monthly"
groups:
npm:
applies-to: version-updates
Expand All @@ -12,7 +12,7 @@ updates:
- package-ecosystem: "maven" # See documentation for possible values
directory: "/backend" # Location of package manifests
schedule:
interval: "weekly"
interval: "monthly"
groups:
npm:
applies-to: version-updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public enum MessageType {
SET_RULES,
CHANGE_GAME_STATES,
UPDATE_TEAMS,
UPDATE_GAME_ACTION,
PLAY_GAME_ACTION,

//Notification message types
INIT_NOTIFICATION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@

import fr.zelytra.game.manager.message.MessageType;
import fr.zelytra.game.manager.message.SocketMessage;
import fr.zelytra.game.pool.*;
import fr.zelytra.game.pool.PoolParty;
import fr.zelytra.game.pool.PoolPlayer;
import fr.zelytra.game.pool.data.*;
import fr.zelytra.game.pool.game.PoolVictoryState;
import fr.zelytra.notification.Notification;
import fr.zelytra.notification.NotificationMessageKey;
import fr.zelytra.notification.NotificationMessageType;
import fr.zelytra.notification.NotificationSocket;
import fr.zelytra.user.UserEntity;
import fr.zelytra.user.UserService;
import io.quarkus.arc.Lock;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.websocket.Session;

import java.util.Objects;
Expand Down Expand Up @@ -74,7 +82,7 @@ public void joinPool(String username, String sessionID, Session socketSession) {

if (sessionID.isBlank() || sessionID.isEmpty()) {
PoolParty poolParty = createParty(username, socketSession);
SocketMessage<PoolParty> socketMessage = new SocketMessage<>(MessageType.UPDATE_POOL_DATA,poolParty);
SocketMessage<PoolParty> socketMessage = new SocketMessage<>(MessageType.UPDATE_POOL_DATA, poolParty);
socketMessage.sendDataToPlayer(poolPlayer.getSocketSession());
return;
} else if (isPlayerInPool(username)) {
Expand Down Expand Up @@ -269,7 +277,7 @@ public void setRule(GameRules gameRules, String socketSessionId) {
/**
* Sets the game status for a specific pool party.
*
* @param gameStatus the game rules to set
* @param gameStatus the game rules to set
* @param socketSessionId the socket session ID of the player setting the rules
*/
@Lock(value = Lock.Type.WRITE, time = 200)
Expand All @@ -284,7 +292,7 @@ public void setStatus(GameStatus gameStatus, String socketSessionId) {
/**
* Sets the teams for a specific pool party.
*
* @param team the teams of the party
* @param team the teams of the party
* @param socketSessionId the socket session ID of the player setting the rules
*/
@Lock(value = Lock.Type.WRITE, time = 200)
Expand All @@ -296,6 +304,43 @@ public void setPlayersTeam(PoolTeam team, String socketSessionId) {
Log.info("[setPlayersTeam][" + poolParty.getUuid() + "] User: " + poolPlayer.getUsername() + " set new teams !");
}

@Lock(value = Lock.Type.WRITE, time = 200)
public void updateCurrentGameAction(GameAction gameAction, String socketSessionId) {
PoolPlayer poolPlayer = getPlayerBySocketSessionId(socketSessionId);
PoolParty poolParty = getPoolPartyByPlayer(poolPlayer.getUsername());
poolParty.setCurrentAction(gameAction);
broadcastPoolDataToParty(poolParty);
Log.info("[updateCurrentGameAction][" + poolParty.getUuid() + "] User: " + poolPlayer.getUsername() + " update current game action");
}

@Lock(value = Lock.Type.WRITE, time = 200)
@Transactional
public void playAction(GameAction gameAction, String socketSessionId) {
PoolPlayer poolPlayer = getPlayerBySocketSessionId(socketSessionId);
PoolParty poolParty = getPoolPartyByPlayer(poolPlayer.getUsername());

poolParty.getGame().play(gameAction);
PoolVictoryState victoryState = poolParty.getGame().winDetection();

if (victoryState != PoolVictoryState.NONE) {
GameReport gameReport = poolParty.winHandler(victoryState);
poolParty.setGameReport(gameReport);

// Updating players data in DB
for (PoolPlayer x : poolParty.getPlayers()) {
UserEntity user = userService.getUserByName(x.getUsername());
GameReportPlayer reportPlayer = gameReport.getFromUsername(x.getAuthUsername());
if (reportPlayer == null) {
continue;
}
user.setPp(reportPlayer.pp());
user.setGamePlayed(user.getGamePlayed() + 1);
}
}
broadcastPoolDataToParty(poolParty);
Log.info("[playAction][" + poolParty.getUuid() + "] User: " + poolPlayer.getUsername() + " play game action");
}

/**
* Retrieves all active pool parties.
*
Expand Down Expand Up @@ -323,11 +368,24 @@ public void broadcastPoolDataToParty(PoolParty poolParty) {
socketMessage.sendDataToPlayer(player.getSocketSession());
Log.info("[broadcastPoolDataToParty][" + poolParty.getUuid() + "] Data sent to: " + player.getUsername());
}

// Ensure the game owner also receives the data
SocketMessage<PoolParty> socketMessage = new SocketMessage<>(MessageType.UPDATE_POOL_DATA, poolParty);
socketMessage.sendDataToPlayer(poolParty.getGameOwner().getSocketSession());
Log.info("[broadcastPoolDataToParty][" + poolParty.getUuid() + "] Data sent to game owner: " + poolParty.getGameOwner().getUsername());
}

/**
* Broadcasts the pool data to all players in the party.
*
* @param poolParty the pool party whose data is to be broadcast
*/
@Lock(value = Lock.Type.WRITE, time = 200)
public static void broadcastNotificationToParty(PoolParty poolParty, NotificationMessageKey messageKey) {
if (poolParty == null) {
Log.warn("[broadcastNotificationToParty][N/A] Pool party is null");
return;
}

Notification<String> message = new Notification<>(NotificationMessageType.MESSAGE, messageKey.getKey());
for (PoolPlayer player : poolParty.getPlayers()) {
NotificationSocket.sendNotification(message, player.getAuthUsername());
Log.info("[broadcastNotificationToParty][" + poolParty.getUuid() + "] Data sent to: " + player.getUsername());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import com.fasterxml.jackson.core.type.TypeReference;
import fr.zelytra.game.manager.message.SocketMessage;
import fr.zelytra.game.manager.message.SocketTimeOutManager;
import fr.zelytra.game.pool.GameRules;
import fr.zelytra.game.pool.GameStatus;
import fr.zelytra.game.pool.PoolTeam;
import fr.zelytra.game.pool.data.GameAction;
import fr.zelytra.game.pool.data.GameRules;
import fr.zelytra.game.pool.data.GameStatus;
import fr.zelytra.game.pool.data.PoolTeam;
import io.quarkus.logging.Log;
import jakarta.inject.Inject;
import jakarta.websocket.*;
Expand Down Expand Up @@ -57,6 +58,12 @@ public void onMessage(String message, Session session, @PathParam("sessionId") S
case UPDATE_TEAMS -> {
socketService.setPlayersTeam(objectMapper.convertValue(socketMessage.data(), PoolTeam.class), session.getId());
}
case UPDATE_GAME_ACTION -> {
socketService.updateCurrentGameAction(objectMapper.convertValue(socketMessage.data(), GameAction.class), session.getId());
}
case PLAY_GAME_ACTION -> {
socketService.playAction(objectMapper.convertValue(socketMessage.data(), GameAction.class), session.getId());
}
case CHANGE_GAME_STATES -> {
GameStatus status = objectMapper.convertValue(socketMessage.data(), GameStatus.class);
socketService.setStatus(status, session.getId());
Expand Down
94 changes: 81 additions & 13 deletions backend/src/main/java/fr/zelytra/game/pool/PoolParty.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package fr.zelytra.game.pool;

import fr.zelytra.game.manager.socket.PoolSocketService;
import fr.zelytra.game.pool.data.*;
import fr.zelytra.game.pool.game.AmericanEightPoolGame;
import fr.zelytra.game.pool.game.PoolGameInterface;
import fr.zelytra.game.pool.game.PoolVictoryState;
import fr.zelytra.notification.NotificationMessageKey;
import fr.zelytra.poolpoint.PoolPointCalculator;
import fr.zelytra.user.UserEntity;

import java.util.ArrayList;
Expand All @@ -13,14 +20,13 @@ public class PoolParty {
private PoolPlayer gameOwner;
private GameRules rules;
private GameStatus state;
private int maxPlayerAmount;
private final PoolTeam teams;
private PoolGameInterface game;
private GameReport gameReport;

public PoolParty(PoolPlayer user) {
this.gameOwner = user;
players.add(user);
state = GameStatus.SETUP;
teams = new PoolTeam(new ArrayList<>(), new ArrayList<>());
}

public List<PoolPlayer> getPlayers() {
Expand All @@ -39,20 +45,43 @@ public GameRules getRules() {
return rules;
}

public void setGame() {
switch (rules) {
case AMERICAN_8:
game = new AmericanEightPoolGame();
break;
default:
throw new IllegalArgumentException("Unsupported game type: " + rules);
}

}

public void setRules(GameRules rules) {
this.rules = rules;
this.maxPlayerAmount = rules.maxTotalPlayer;
setGame();
}

public void setCurrentAction(GameAction action) {
this.game.setCurrentAction(action);
}

public GameReport getGameReport() {
return gameReport;
}

public void setGameReport(GameReport gameReport) {
this.gameReport = gameReport;
}

public boolean setTeams(PoolTeam teams) {
// Limit player amount by rules
if (teams.team1().size() + teams.team2().size() > maxPlayerAmount) {
if (teams.team1().size() + teams.team2().size() > rules.maxTotalPlayer) {
return false;
}
this.teams.team1().clear();
this.teams.team2().clear();
this.teams.team1().addAll(teams.team1());
this.teams.team2().addAll(teams.team2());
this.game.getTeams().team1().clear();
this.game.getTeams().team2().clear();
this.game.getTeams().team1().addAll(teams.team1());
this.game.getTeams().team2().addAll(teams.team2());
return true;
}

Expand All @@ -63,9 +92,11 @@ public GameStatus getState() {
public boolean setState(GameStatus state) {
// No empty teams
if (this.state == GameStatus.TEAMING_PLAYERS && state == GameStatus.RUNNING) {
if (this.teams.team1().isEmpty() || this.teams.team2().isEmpty()) {
if (this.game.getTeams().team1().isEmpty() || this.game.getTeams().team2().isEmpty()) {
PoolSocketService.broadcastNotificationToParty(this, NotificationMessageKey.EMPTY_TEAM);
return false;
}
game.initGame();
}
this.state = state;
return true;
Expand All @@ -76,11 +107,14 @@ public String getUuid() {
}

public int getMaxPlayerAmount() {
return maxPlayerAmount;
if (rules == null) {
return 0;
}
return rules.maxTotalPlayer;
}

public PoolTeam getTeams() {
return teams;
public PoolGameInterface getGame() {
return game;
}

/**
Expand All @@ -99,4 +133,38 @@ public boolean removePlayer(UserEntity user) {
}
return false;
}

public GameReport winHandler(PoolVictoryState victoryState) {
game.setVictoryState(victoryState);
setState(GameStatus.END);
GameReport gameReport = new GameReport(new ArrayList<>(), new ArrayList<>());
for (PoolPlayer player : players) {
PoolPointCalculator poolPointCalculator = new PoolPointCalculator(player.getPp(), player.getGamePlayed());
List<Integer> opponentPPs = getPoolPlayersByTeam(victoryState.getInvertTeam()).stream().map(UserEntity::getPp).toList();

if ((victoryState == PoolVictoryState.TEAM1 && game.getTeams().team1().contains(player.getAuthUsername())) ||
(victoryState == PoolVictoryState.TEAM2 && game.getTeams().team2().contains(player.getAuthUsername()))) {
// Player is in the winning team
int newPlayerPP = poolPointCalculator.computeNewElo(1.0, opponentPPs);
gameReport.victoryPlayer().add(new GameReportPlayer(player.getPp(), newPlayerPP, player.getUsername()));
} else {
// Player is in the losing team
int newPlayerPP = poolPointCalculator.computeNewElo(0, opponentPPs);
gameReport.looserPlayer().add(new GameReportPlayer(player.getPp(), newPlayerPP, player.getUsername()));
}
}
return gameReport;
}

public List<PoolPlayer> getPoolPlayersByTeam(PoolVictoryState poolVictoryState) {
List<PoolPlayer> poolPlayers = new ArrayList<>();
for (PoolPlayer player : players) {
if (poolVictoryState == PoolVictoryState.TEAM1 && game.getTeams().team1().contains(player.getAuthUsername())) {
poolPlayers.add(player);
} else if (poolVictoryState == PoolVictoryState.TEAM2 && game.getTeams().team2().contains(player.getAuthUsername())) {
poolPlayers.add(player);
}
}
return poolPlayers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package fr.zelytra.game.pool.data;

import java.util.List;

public record GameAction(int roundId, List<PoolBalls> balls, List<PoolFault> faults, String username) {
}
20 changes: 20 additions & 0 deletions backend/src/main/java/fr/zelytra/game/pool/data/GameReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fr.zelytra.game.pool.data;

import java.util.List;

public record GameReport(List<GameReportPlayer> victoryPlayer, List<GameReportPlayer> looserPlayer) {

public GameReportPlayer getFromUsername(String username) {
for (GameReportPlayer victoryPlayer : victoryPlayer) {
if (victoryPlayer.username().equals(username)) {
return victoryPlayer;
}
}
for (GameReportPlayer looserPlayer : looserPlayer) {
if (looserPlayer.username().equals(username)) {
return looserPlayer;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package fr.zelytra.game.pool.data;

public record GameReportPlayer(int previousPP, int pp, String username) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.zelytra.game.pool;
package fr.zelytra.game.pool.data;

public enum GameRules {
AMERICAN_8(6);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.zelytra.game.pool;
package fr.zelytra.game.pool.data;

public enum GameStatus {
SETUP,
Expand Down
Loading

0 comments on commit 7f8a290

Please sign in to comment.