@@ -26,6 +26,9 @@ public class ClientController {
|
|||||||
/** Network client (either TCP or RMI) */
|
/** Network client (either TCP or RMI) */
|
||||||
private IClient client;
|
private IClient client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The username of the client associated with this event.
|
||||||
|
*/
|
||||||
public String myUsername;
|
public String myUsername;
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +68,11 @@ public class ClientController {
|
|||||||
view.setModel(miniModel);
|
view.setModel(miniModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the username of the client associated with this event.
|
||||||
|
*
|
||||||
|
* @param username the username to set.
|
||||||
|
*/
|
||||||
public void setMyUsername (String username) {
|
public void setMyUsername (String username) {
|
||||||
this.myUsername=username;
|
this.myUsername=username;
|
||||||
}
|
}
|
||||||
@@ -170,7 +178,16 @@ public class ClientController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Handles the selection of a totem by the specified player.
|
||||||
|
*
|
||||||
|
* <p>If the player is not the current one, an error message is shown.
|
||||||
|
* Otherwise, the selected totem is retrieved from the available totems
|
||||||
|
* and the choice is forwarded to the client.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the choice.
|
||||||
|
* @param pos the position of the selected totem in the available totems list.
|
||||||
|
*/
|
||||||
public void totemChoice(String playerUsername,int pos) {
|
public void totemChoice(String playerUsername,int pos) {
|
||||||
if(!Objects.equals(playerUsername, miniModel.currentState.getCurrentPlayer().getUserName()))
|
if(!Objects.equals(playerUsername, miniModel.currentState.getCurrentPlayer().getUserName()))
|
||||||
view.showError("It's not your turn!");
|
view.showError("It's not your turn!");
|
||||||
|
|||||||
@@ -30,7 +30,14 @@ public class GameController {
|
|||||||
*/
|
*/
|
||||||
public GameController() {
|
public GameController() {
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Marks the player associated with the specified username as disconnected.
|
||||||
|
*
|
||||||
|
* @param username the username of the player who disconnected.
|
||||||
|
* @return {@code true} if the disconnection is handled successfully,
|
||||||
|
* {@code false} if no player with the specified username exists.
|
||||||
|
*/
|
||||||
public boolean DisconnectedPlayer(String username)
|
public boolean DisconnectedPlayer(String username)
|
||||||
{
|
{
|
||||||
Player player= model.getPlayerByUsername(username);
|
Player player= model.getPlayerByUsername(username);
|
||||||
@@ -38,7 +45,14 @@ public class GameController {
|
|||||||
return false;
|
return false;
|
||||||
return model.DisconnectedPlayer(player);
|
return model.DisconnectedPlayer(player);
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Marks the player associated with the specified username as reconnected.
|
||||||
|
*
|
||||||
|
* @param username the username of the player who reconnected.
|
||||||
|
* @return {@code true} if the reconnection is handled successfully,
|
||||||
|
* {@code false} if no player with the specified username exists.
|
||||||
|
*/
|
||||||
public boolean ReconnectPlayer(String username)
|
public boolean ReconnectPlayer(String username)
|
||||||
{
|
{
|
||||||
Player player= model.getPlayerByUsername(username);
|
Player player= model.getPlayerByUsername(username);
|
||||||
@@ -46,6 +60,7 @@ public class GameController {
|
|||||||
return false;
|
return false;
|
||||||
return model.ReconnectPlayer(player);
|
return model.ReconnectPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the game model managed by this controller.
|
* Returns the game model managed by this controller.
|
||||||
*
|
*
|
||||||
@@ -166,7 +181,14 @@ public class GameController {
|
|||||||
return model.SlotChoiceByIndex(model.getPlayerByUsername(playerUsername),pos);
|
return model.SlotChoiceByIndex(model.getPlayerByUsername(playerUsername),pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Applies the totem choice made by the player associated with the specified username.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the choice.
|
||||||
|
* @param totem the name of the selected totem.
|
||||||
|
* @return {@code true} if the choice is handled successfully,
|
||||||
|
* {@code false} if no player with the specified username exists.
|
||||||
|
*/
|
||||||
public boolean TotemChoice(String playerUsername,String totem) {
|
public boolean TotemChoice(String playerUsername,String totem) {
|
||||||
Player player= model.getPlayerByUsername(playerUsername);
|
Player player= model.getPlayerByUsername(playerUsername);
|
||||||
if(player==null)
|
if(player==null)
|
||||||
|
|||||||
@@ -4,5 +4,34 @@ package it.polimi.ingsw.gc14.Model.Cards.Building;
|
|||||||
* Represents the possible effect types of building cards.
|
* Represents the possible effect types of building cards.
|
||||||
*/
|
*/
|
||||||
public enum EffectType {
|
public enum EffectType {
|
||||||
FINAL, CARD_SET, INVENTOR_PAIR, ON_EVENT, ON_END_TURN, ON_ROUND_END
|
|
||||||
|
/**
|
||||||
|
* Effect applied during the final scoring phase.
|
||||||
|
*/
|
||||||
|
FINAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect based on collecting a specific set of cards.
|
||||||
|
*/
|
||||||
|
CARD_SET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect based on pairs of Inventor cards.
|
||||||
|
*/
|
||||||
|
INVENTOR_PAIR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect triggered when an event card is resolved.
|
||||||
|
*/
|
||||||
|
ON_EVENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect triggered at the end of a player's turn.
|
||||||
|
*/
|
||||||
|
ON_END_TURN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect triggered at the end of a round.
|
||||||
|
*/
|
||||||
|
ON_ROUND_END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,34 @@ package it.polimi.ingsw.gc14.Model.Cards.TribeCards;
|
|||||||
* Represents the different types of character cards available in the game.
|
* Represents the different types of character cards available in the game.
|
||||||
*/
|
*/
|
||||||
public enum CharacterType {
|
public enum CharacterType {
|
||||||
INVENTOR, BUILDER, GATHERER, ARTIST, SHAMAN, HUNTER
|
|
||||||
|
/**
|
||||||
|
* Character card representing an Inventor.
|
||||||
|
*/
|
||||||
|
INVENTOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character card representing a Builder.
|
||||||
|
*/
|
||||||
|
BUILDER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character card representing a Gatherer.
|
||||||
|
*/
|
||||||
|
GATHERER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character card representing an Artist.
|
||||||
|
*/
|
||||||
|
ARTIST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character card representing a Shaman.
|
||||||
|
*/
|
||||||
|
SHAMAN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character card representing a Hunter.
|
||||||
|
*/
|
||||||
|
HUNTER
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,24 @@ package it.polimi.ingsw.gc14.Model.Cards.TribeCards;
|
|||||||
* Represents the different types of event cards available in the game.
|
* Represents the different types of event cards available in the game.
|
||||||
*/
|
*/
|
||||||
public enum EventType {
|
public enum EventType {
|
||||||
SUSTENANCE, SHAMANIC_RITUAL, CAVE_PAINTINGS, HUNT
|
|
||||||
|
/**
|
||||||
|
* Event card representing Sustenance.
|
||||||
|
*/
|
||||||
|
SUSTENANCE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event card representing the Shamanic Ritual.
|
||||||
|
*/
|
||||||
|
SHAMANIC_RITUAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event card representing Cave Paintings.
|
||||||
|
*/
|
||||||
|
CAVE_PAINTINGS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event card representing the Hunt.
|
||||||
|
*/
|
||||||
|
HUNT
|
||||||
}
|
}
|
||||||
@@ -29,11 +29,20 @@ import java.util.stream.Stream;
|
|||||||
*/
|
*/
|
||||||
public class Game implements Serializable {
|
public class Game implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The final ranking of players at the end of the game.
|
||||||
|
*/
|
||||||
private ArrayList<Player> playerStanding;
|
private ArrayList<Player> playerStanding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the final ranking of players.
|
||||||
|
*
|
||||||
|
* @return the list of players ordered according to their final standing.
|
||||||
|
*/
|
||||||
public ArrayList<Player> getPlayerStanding() {
|
public ArrayList<Player> getPlayerStanding() {
|
||||||
return playerStanding;
|
return playerStanding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of players participating in the game.
|
* Returns the list of players participating in the game.
|
||||||
*
|
*
|
||||||
@@ -42,16 +51,38 @@ public class Game implements Serializable {
|
|||||||
public List<Player> getPlayers() {
|
public List<Player> getPlayers() {
|
||||||
return playersList;
|
return playersList;
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Returns the list of totems that have not yet been assigned to any player.
|
||||||
|
*
|
||||||
|
* @return the list of currently available totems.
|
||||||
|
*/
|
||||||
public List<Totems>getAvailableTotems() {
|
public List<Totems>getAvailableTotems() {
|
||||||
List<Totems> totems=new ArrayList<>(List.of(Totems.values()));
|
List<Totems> totems=new ArrayList<>(List.of(Totems.values()));
|
||||||
playersList.forEach(player -> {if(player.totem!=null)totems.remove(player.totem);});
|
playersList.forEach(player -> {if(player.totem!=null)totems.remove(player.totem);});
|
||||||
return totems;
|
return totems;
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Queue containing the players who still have to choose their totem.
|
||||||
|
*/
|
||||||
private Queue<Player> totemChoiceQueue = new LinkedList<>();
|
private Queue<Player> totemChoiceQueue = new LinkedList<>();
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Assigns the selected totem to the specified player during the totem choice phase.
|
||||||
|
*
|
||||||
|
* <p>The choice is accepted only if the game is currently in the
|
||||||
|
* {@link GameStages#TOTEM_CHOICE} stage, the player is the current one,
|
||||||
|
* and the selected totem is still available.
|
||||||
|
*
|
||||||
|
* <p>After a valid choice, the method advances to the next player in the queue.
|
||||||
|
* If all players have completed the selection, the game moves to the slot choice phase.
|
||||||
|
* Disconnected players are automatically assigned a random available totem.
|
||||||
|
*
|
||||||
|
* @param player the player making the totem choice.
|
||||||
|
* @param totem the selected totem.
|
||||||
|
* @return {@code true} if the choice is applied successfully, {@code false} otherwise.
|
||||||
|
*/
|
||||||
public boolean TotemChoice(Player player,Totems totem) {
|
public boolean TotemChoice(Player player,Totems totem) {
|
||||||
if(!currentState.getGameStage().equals(GameStages.TOTEM_CHOICE))
|
if(!currentState.getGameStage().equals(GameStages.TOTEM_CHOICE))
|
||||||
return false;
|
return false;
|
||||||
@@ -87,9 +118,23 @@ public class Game implements Serializable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Map tracking the players who are currently disconnected.
|
||||||
|
*/
|
||||||
public Map<Player,Boolean> disconnetedPlayers = new HashMap<>();
|
public Map<Player,Boolean> disconnetedPlayers = new HashMap<>();
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Marks the specified player as disconnected and updates the game flow accordingly.
|
||||||
|
*
|
||||||
|
* <p>If the player disconnects during the waiting phase, they are removed from
|
||||||
|
* the player list and from the totem choice queue. If the disconnected player is
|
||||||
|
* the current one, the game advances to the next suitable player or, during the
|
||||||
|
* totem choice phase, handles the remaining selection flow automatically.
|
||||||
|
*
|
||||||
|
* @param player the player who disconnected.
|
||||||
|
* @return {@code true} if the disconnection is handled successfully,
|
||||||
|
* {@code false} if the player was already marked as disconnected.
|
||||||
|
*/
|
||||||
public boolean DisconnectedPlayer(Player player)
|
public boolean DisconnectedPlayer(Player player)
|
||||||
{
|
{
|
||||||
if(disconnetedPlayers.containsKey(player) && disconnetedPlayers.get(player))
|
if(disconnetedPlayers.containsKey(player) && disconnetedPlayers.get(player))
|
||||||
@@ -121,7 +166,17 @@ public class Game implements Serializable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Marks the specified player as reconnected.
|
||||||
|
*
|
||||||
|
* <p>If the player reconnects during the slot choice phase, they are removed
|
||||||
|
* from the disconnected players map and reinserted into the order logic card
|
||||||
|
* when necessary.
|
||||||
|
*
|
||||||
|
* @param player the player who reconnected.
|
||||||
|
* @return {@code true} if the reconnection is handled successfully,
|
||||||
|
* {@code false} if the player was not previously marked as disconnected.
|
||||||
|
*/
|
||||||
public boolean ReconnectPlayer(Player player)
|
public boolean ReconnectPlayer(Player player)
|
||||||
{
|
{
|
||||||
if(!disconnetedPlayers.containsKey(player))
|
if(!disconnetedPlayers.containsKey(player))
|
||||||
@@ -140,12 +195,14 @@ public class Game implements Serializable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Totem
|
/**
|
||||||
//TODO
|
* Clears the collection of disconnected players.
|
||||||
|
*/
|
||||||
public void ClearDisconnected()
|
public void ClearDisconnected()
|
||||||
{
|
{
|
||||||
disconnetedPlayers.clear();
|
disconnetedPlayers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current number of players participating in the game.
|
* Returns the current number of players participating in the game.
|
||||||
* @return the current number of players.
|
* @return the current number of players.
|
||||||
@@ -195,7 +252,12 @@ public class Game implements Serializable {
|
|||||||
* The board associated with this game.
|
* The board associated with this game.
|
||||||
*/
|
*/
|
||||||
private Board board;
|
private Board board;
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Returns the game board.
|
||||||
|
*
|
||||||
|
* @return the board associated with the game.
|
||||||
|
*/
|
||||||
public Board getBoard() {return board;}
|
public Board getBoard() {return board;}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -925,6 +987,13 @@ public class Game implements Serializable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the game by forfeit and determines the final player standing.
|
||||||
|
*
|
||||||
|
* <p>The player who is still connected is declared the winner and placed
|
||||||
|
* in the first position of the final ranking. The remaining players are
|
||||||
|
* ordered by prestige value and, in case of a tie, by food value.
|
||||||
|
*/
|
||||||
public void EndGameForFeit() {
|
public void EndGameForFeit() {
|
||||||
Player winner=playersList.stream().filter(x->!disconnetedPlayers.containsKey(x)||!disconnetedPlayers.get(x)).toList().get(0);
|
Player winner=playersList.stream().filter(x->!disconnetedPlayers.containsKey(x)||!disconnetedPlayers.get(x)).toList().get(0);
|
||||||
currentState.GameStageUpdate(GameStages.ENDED);
|
currentState.GameStageUpdate(GameStages.ENDED);
|
||||||
|
|||||||
@@ -4,5 +4,43 @@ package it.polimi.ingsw.gc14.Model.GamePackage;
|
|||||||
* Represents the possible stages of a game.
|
* Represents the possible stages of a game.
|
||||||
*/
|
*/
|
||||||
public enum GameStages {
|
public enum GameStages {
|
||||||
WAITING,TOTEM_CHOICE, SLOT_CHOICE, RES_ACTIONS, OPT_CARD_E, RES_EVENT, ENDING, ENDED
|
/**
|
||||||
|
* Stage in which the game is waiting for players to join.
|
||||||
|
*/
|
||||||
|
WAITING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which players choose their totems.
|
||||||
|
*/
|
||||||
|
TOTEM_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which players select their action slots.
|
||||||
|
*/
|
||||||
|
SLOT_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which the mandatory actions associated with the chosen slots are resolved.
|
||||||
|
*/
|
||||||
|
RES_ACTIONS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which an optional card effect can be resolved.
|
||||||
|
*/
|
||||||
|
OPT_CARD_E,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which an event card is being resolved.
|
||||||
|
*/
|
||||||
|
RES_EVENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage in which end-of-round or end-of-game operations are processed.
|
||||||
|
*/
|
||||||
|
ENDING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage indicating that the game has ended.
|
||||||
|
*/
|
||||||
|
ENDED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,86 @@
|
|||||||
package it.polimi.ingsw.gc14.Model;
|
package it.polimi.ingsw.gc14.Model;
|
||||||
|
|
||||||
import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
|
|
||||||
import it.polimi.ingsw.gc14.Model.Cards.TribeCard;
|
|
||||||
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.Character;
|
|
||||||
import it.polimi.ingsw.gc14.Model.GamePackage.Board;
|
import it.polimi.ingsw.gc14.Model.GamePackage.Board;
|
||||||
import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState;
|
import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight representation of the game model shared with clients.
|
||||||
|
*
|
||||||
|
* <p>The mini model contains only the information required by the client-side
|
||||||
|
* view and controllers to render the current match state and process incoming
|
||||||
|
* network updates.
|
||||||
|
*/
|
||||||
public class MiniModel implements Serializable {
|
public class MiniModel implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current game board.
|
||||||
|
*/
|
||||||
public Board board;
|
public Board board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map associating each occupied slot with the corresponding player.
|
||||||
|
*/
|
||||||
public Map<Slot, Player> slotPlayerMap;
|
public Map<Slot, Player> slotPlayerMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card used to manage the player turn order.
|
||||||
|
*/
|
||||||
public OrderLogicCard orderLogicCard;
|
public OrderLogicCard orderLogicCard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of the game.
|
||||||
|
*/
|
||||||
public CurrentState currentState;
|
public CurrentState currentState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of players indexed by username.
|
||||||
|
*/
|
||||||
public Map<String, Player> players;
|
public Map<String, Player> players;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of totems still available for selection.
|
||||||
|
*/
|
||||||
public List<Totems> availableTotems;
|
public List<Totems> availableTotems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final standing of the players at the end of the game.
|
||||||
|
*/
|
||||||
public List<Player> standingPlayers;
|
public List<Player> standingPlayers;
|
||||||
|
|
||||||
// --- Costruttori ---
|
/**
|
||||||
|
* Constructs an empty mini model.
|
||||||
|
*/
|
||||||
public MiniModel() {
|
public MiniModel() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a mini model containing only the game board.
|
||||||
|
*
|
||||||
|
* @param board the current game board.
|
||||||
|
*/
|
||||||
public MiniModel(Board board) {
|
public MiniModel(Board board) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MiniModel(Board board, Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard,
|
/**
|
||||||
CurrentState currentState, List<Player> players, List<Totems> availableTotems) {
|
* Constructs a complete mini model from the current server-side game data.
|
||||||
|
*
|
||||||
|
* @param board the current game board.
|
||||||
|
* @param slotPlayerMap the map associating occupied slots with players.
|
||||||
|
* @param orderLogicCard the card managing player turn order.
|
||||||
|
* @param currentState the current state of the game.
|
||||||
|
* @param players the list of players in the match.
|
||||||
|
* @param availableTotems the list of totems still available for selection.
|
||||||
|
*/
|
||||||
|
public MiniModel(Board board,
|
||||||
|
Map<Slot, Player> slotPlayerMap,
|
||||||
|
OrderLogicCard orderLogicCard,
|
||||||
|
CurrentState currentState,
|
||||||
|
List<Player> players,
|
||||||
|
List<Totems> availableTotems) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
this.orderLogicCard = orderLogicCard;
|
this.orderLogicCard = orderLogicCard;
|
||||||
@@ -42,43 +91,90 @@ public class MiniModel implements Serializable {
|
|||||||
setPlayers(players);
|
setPlayers(players);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// --- Setter ---
|
* Sets the current game board.
|
||||||
|
*
|
||||||
|
* @param board the board to assign.
|
||||||
|
*/
|
||||||
public void setBoard(Board board) {
|
public void setBoard(Board board) {
|
||||||
this.board = board;
|
this.board = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of totems still available for selection.
|
||||||
|
*
|
||||||
|
* @param availableTotems the available totems.
|
||||||
|
*/
|
||||||
public void setAvailableTotems(List<Totems> availableTotems) {
|
public void setAvailableTotems(List<Totems> availableTotems) {
|
||||||
this.availableTotems = availableTotems;
|
this.availableTotems = availableTotems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the map associating occupied slots with players.
|
||||||
|
*
|
||||||
|
* @param slotPlayerMap the slot-player map to assign.
|
||||||
|
*/
|
||||||
public void setSlotPlayerMap(Map<Slot, Player> slotPlayerMap) {
|
public void setSlotPlayerMap(Map<Slot, Player> slotPlayerMap) {
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the card used to manage the player turn order.
|
||||||
|
*
|
||||||
|
* @param orderLogicCard the order logic card to assign.
|
||||||
|
*/
|
||||||
public void setOrderLogicCard(OrderLogicCard orderLogicCard) {
|
public void setOrderLogicCard(OrderLogicCard orderLogicCard) {
|
||||||
this.orderLogicCard = orderLogicCard;
|
this.orderLogicCard = orderLogicCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current state of the game.
|
||||||
|
*
|
||||||
|
* @param currentState the current game state.
|
||||||
|
*/
|
||||||
public void setCurrentState(CurrentState currentState) {
|
public void setCurrentState(CurrentState currentState) {
|
||||||
this.currentState = currentState;
|
this.currentState = currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces or updates the players stored in the mini model.
|
||||||
|
*
|
||||||
|
* <p>Each player is indexed by username.
|
||||||
|
*
|
||||||
|
* @param players the list of players to store.
|
||||||
|
*/
|
||||||
public void setPlayers(List<Player> players) {
|
public void setPlayers(List<Player> players) {
|
||||||
for (Player player : players) {
|
for (Player player : players) {
|
||||||
this.players.put(player.getUserName(), player);
|
this.players.put(player.getUserName(), player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or updates a single player in the mini model.
|
||||||
|
*
|
||||||
|
* @param player the player to store.
|
||||||
|
*/
|
||||||
public void setPlayer(Player player) {
|
public void setPlayer(Player player) {
|
||||||
players.put(player.getUserName(), player);
|
players.put(player.getUserName(), player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the final standing of the players.
|
||||||
|
*
|
||||||
|
* @param standingPlayers the final ordered list of players.
|
||||||
|
*/
|
||||||
public void setStandingPlayers(ArrayList<Player> standingPlayers) {
|
public void setStandingPlayers(ArrayList<Player> standingPlayers) {
|
||||||
this.standingPlayers = standingPlayers;
|
this.standingPlayers = standingPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the player with the specified username
|
||||||
|
* within the player collection.
|
||||||
|
*
|
||||||
|
* @param username the username of the player to search for.
|
||||||
|
* @return the zero-based position of the player, or {@code -1}
|
||||||
|
* if no matching username is found.
|
||||||
|
*/
|
||||||
public int getPositionByUsername(String username) {
|
public int getPositionByUsername(String username) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Player player : players.values()) {
|
for (Player player : players.values()) {
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
package it.polimi.ingsw.gc14.Model;
|
package it.polimi.ingsw.gc14.Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the different totem colors available in the game.
|
||||||
|
*/
|
||||||
public enum Totems {
|
public enum Totems {
|
||||||
YELLOW, BLUE, ORANGE, WHITE, PURPLE
|
|
||||||
|
/**
|
||||||
|
* Yellow totem.
|
||||||
|
*/
|
||||||
|
YELLOW,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blue totem.
|
||||||
|
*/
|
||||||
|
BLUE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orange totem.
|
||||||
|
*/
|
||||||
|
ORANGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* White totem.
|
||||||
|
*/
|
||||||
|
WHITE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purple totem.
|
||||||
|
*/
|
||||||
|
PURPLE
|
||||||
}
|
}
|
||||||
@@ -5,16 +5,64 @@ package it.polimi.ingsw.gc14.Network;
|
|||||||
* client and server.
|
* client and server.
|
||||||
*/
|
*/
|
||||||
public enum EventType {
|
public enum EventType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to add a player to the game.
|
||||||
|
*/
|
||||||
ADD_PLAYER,
|
ADD_PLAYER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to submit a player's totem choice.
|
||||||
|
*/
|
||||||
TOTEM_CHOICE,
|
TOTEM_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to submit a player's slot choice.
|
||||||
|
*/
|
||||||
SLOT_CHOICE,
|
SLOT_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to draw a tribe card from the upper card list.
|
||||||
|
*/
|
||||||
DRAW_UPPER_TRIBE,
|
DRAW_UPPER_TRIBE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to draw a tribe card from the lower card list.
|
||||||
|
*/
|
||||||
DRAW_LOWER_TRIBE,
|
DRAW_LOWER_TRIBE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to draw a building card from the upper card list.
|
||||||
|
*/
|
||||||
DRAW_UPPER_BUILD,
|
DRAW_UPPER_BUILD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to draw a building card from the lower card list.
|
||||||
|
*/
|
||||||
DRAW_LOWER_BUILD,
|
DRAW_LOWER_BUILD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to skip the current turn or optional action.
|
||||||
|
*/
|
||||||
SKIP_TURN,
|
SKIP_TURN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to notify that a player has disconnected.
|
||||||
|
*/
|
||||||
DISCONNECTED_PLAYER,
|
DISCONNECTED_PLAYER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to notify that a player has reconnected.
|
||||||
|
*/
|
||||||
RECONNECT_PLAYER,
|
RECONNECT_PLAYER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to apply the transition to the next round.
|
||||||
|
*/
|
||||||
NEXT_ROUND,
|
NEXT_ROUND,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to notify that the game has ended.
|
||||||
|
*/
|
||||||
ENDED_GAME
|
ENDED_GAME
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,73 @@
|
|||||||
package it.polimi.ingsw.gc14.Network;
|
package it.polimi.ingsw.gc14.Network;
|
||||||
|
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
|
/**
|
||||||
|
* Defines the operations that a client can request during the game.
|
||||||
import java.rmi.RemoteException;
|
*/
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public interface IClient {
|
public interface IClient {
|
||||||
public boolean connect(String username,int preferredInt);
|
|
||||||
|
|
||||||
public void drawUpperTribeCard(String playerUsername, int pos) ;
|
/**
|
||||||
|
* Connects the client to the server using the specified username
|
||||||
|
* and preferred connection type.
|
||||||
|
*
|
||||||
|
* @param username the username chosen by the player.
|
||||||
|
* @param preferredInt the preferred connection type selected by the client.
|
||||||
|
* @return {@code true} if the connection is established successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean connect(String username, int preferredInt);
|
||||||
|
|
||||||
public void drawLowerTribeCard(String playerUsername,int pos) ;
|
/**
|
||||||
|
* Requests to draw a tribe card from the upper tribe card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card in the upper tribe card list.
|
||||||
|
*/
|
||||||
|
void drawUpperTribeCard(String playerUsername, int pos);
|
||||||
|
|
||||||
public void drawUpperBuildingCard(String playerUsername,int pos) ;
|
/**
|
||||||
|
* Requests to draw a tribe card from the lower tribe card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card in the lower tribe card list.
|
||||||
|
*/
|
||||||
|
void drawLowerTribeCard(String playerUsername, int pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to draw a building card from the upper building card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card in the upper building card list.
|
||||||
|
*/
|
||||||
|
void drawUpperBuildingCard(String playerUsername, int pos);
|
||||||
|
|
||||||
public void drawLowerBuildingCard(String playerUsername,int pos) ;
|
/**
|
||||||
|
* Requests to draw a building card from the lower building card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card in the lower building card list.
|
||||||
|
*/
|
||||||
|
void drawLowerBuildingCard(String playerUsername, int pos);
|
||||||
|
|
||||||
public void skipTurn(String playerUsername);
|
/**
|
||||||
|
* Requests to skip the current player's optional action.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player skipping the action.
|
||||||
|
*/
|
||||||
|
void skipTurn(String playerUsername);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to assign the specified player to a slot.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the slot choice.
|
||||||
|
* @param pos the position of the selected slot.
|
||||||
|
*/
|
||||||
|
void slotChoice(String playerUsername, int pos);
|
||||||
|
|
||||||
public void slotChoice(String playerUsername,int pos) ;
|
/**
|
||||||
public void totemChoice(String playerUsername,String totem) ;
|
* Requests to assign the selected totem to the specified player.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the totem choice.
|
||||||
|
* @param totem the name of the selected totem.
|
||||||
|
*/
|
||||||
|
void totemChoice(String playerUsername, String totem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,59 +11,113 @@ import java.io.Serializable;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an event sent over the network
|
* Represents an event sent over the network.
|
||||||
|
*
|
||||||
|
* <p>A network event contains the information required to identify
|
||||||
|
* the requested action, determine whether it produced an error,
|
||||||
|
* and apply it either to the server-side {@link GameController}
|
||||||
|
* or to the client-side {@link MiniModel}.
|
||||||
*/
|
*/
|
||||||
public abstract class NetworkEvent implements Serializable {
|
public abstract class NetworkEvent implements Serializable {
|
||||||
/** Username of the player requesting the event */
|
|
||||||
|
/**
|
||||||
|
* Username of the player requesting the event.
|
||||||
|
*/
|
||||||
protected String username;
|
protected String username;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the username of the player requesting the event
|
* Returns the username of the player requesting the event.
|
||||||
|
*
|
||||||
|
* @return the username associated with the event.
|
||||||
*/
|
*/
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** EventType of the event */
|
/**
|
||||||
|
* Type of the network event.
|
||||||
|
*/
|
||||||
protected EventType eventType;
|
protected EventType eventType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map associating each occupied slot with the corresponding player.
|
||||||
|
*/
|
||||||
protected Map<Slot, Player> slotPlayerMap;
|
protected Map<Slot, Player> slotPlayerMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order logic card used to manage the player turn order.
|
||||||
|
*/
|
||||||
protected OrderLogicCard orderLogicCard;
|
protected OrderLogicCard orderLogicCard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of the game associated with the event.
|
||||||
|
*/
|
||||||
protected CurrentState currentState;
|
protected CurrentState currentState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player directly involved in the event, when required.
|
||||||
|
*/
|
||||||
protected Player player;
|
protected Player player;
|
||||||
|
|
||||||
public void setData(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState,Player player) {
|
/**
|
||||||
|
* Sets the game data associated with this network event.
|
||||||
|
*
|
||||||
|
* @param slotPlayerMap the map associating each slot with the player occupying it.
|
||||||
|
* @param orderLogicCard the order logic card used to manage turn order.
|
||||||
|
* @param currentState the current state of the game.
|
||||||
|
* @param player the player directly associated with the event.
|
||||||
|
*/
|
||||||
|
public void setData(Map<Slot, Player> slotPlayerMap,
|
||||||
|
OrderLogicCard orderLogicCard,
|
||||||
|
CurrentState currentState,
|
||||||
|
Player player) {
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
this.orderLogicCard = orderLogicCard;
|
this.orderLogicCard = orderLogicCard;
|
||||||
this.currentState = currentState;
|
this.currentState = currentState;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @return the type of the event
|
|
||||||
*/
|
|
||||||
public EventType getEventType() {return eventType;}
|
|
||||||
|
|
||||||
/** Flag signaling whether the event could not be applied to the server model */
|
/**
|
||||||
|
* Returns the type of this event.
|
||||||
|
*
|
||||||
|
* @return the event type.
|
||||||
|
*/
|
||||||
|
public EventType getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating whether the event could not be applied successfully.
|
||||||
|
*/
|
||||||
protected boolean isError;
|
protected boolean isError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the flag signaling whether the event could be applied to the server model
|
* Returns whether the event represents an error.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the event could not be applied successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean getIsError() {return isError;}
|
public boolean getIsError() {
|
||||||
|
return isError;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the isError flag.
|
* Sets whether the event represents an error.
|
||||||
* @param isError the value to set
|
*
|
||||||
|
* @param isError {@code true} if the event could not be applied successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public void setIsError(boolean isError) {this.isError = isError;}
|
public void setIsError(boolean isError) {
|
||||||
|
this.isError = isError;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Constructs a network event.
|
||||||
* Initializes all attributes.
|
*
|
||||||
* @param username the username of the player requesting the event
|
* @param username the username of the player requesting the event.
|
||||||
* @param eventType the type of the event
|
* @param eventType the type of the event.
|
||||||
* @param isError the flag signaling if the event could be applied to the server model
|
* @param isError {@code true} if the event represents an error,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
protected NetworkEvent(String username, EventType eventType, boolean isError) {
|
protected NetworkEvent(String username, EventType eventType, boolean isError) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
@@ -71,26 +125,35 @@ public abstract class NetworkEvent implements Serializable {
|
|||||||
this.isError = isError;
|
this.isError = isError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a string describing the name of the event and whether it is an error or not
|
* Returns a textual description of the event and its outcome.
|
||||||
|
*
|
||||||
|
* @return a string describing the event type and whether it represents an error.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return ("ERROR: action " + eventType.toString());
|
return "ERROR: action " + eventType;
|
||||||
} else {
|
} else {
|
||||||
return ("ACTION: action " + eventType.toString());
|
return "ACTION: action " + eventType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method to apply the current event to the specified Game Controller.
|
* Applies this event to the specified server-side game controller.
|
||||||
* @param gameController the Game Controller on which to apply the event
|
*
|
||||||
* @return true if the event could be applied; false otherwise
|
* @param gameController the game controller on which the event must be applied.
|
||||||
|
* @return {@code true} if the event is applied successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public abstract boolean apply(GameController gameController);
|
public abstract boolean apply(GameController gameController);
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Applies this event to the specified client-side mini model.
|
||||||
|
*
|
||||||
|
* @param model the mini model on which the event must be applied.
|
||||||
|
* @return {@code true} if the event is applied successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
public abstract boolean apply(MiniModel model);
|
public abstract boolean apply(MiniModel model);
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,15 @@ import java.util.Map;
|
|||||||
public class ApplyNextRound extends NetworkEvent implements Serializable{
|
public class ApplyNextRound extends NetworkEvent implements Serializable{
|
||||||
|
|
||||||
List<Player> players;
|
List<Player> players;
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Constructs an event that updates the game state for the next round.
|
||||||
|
*
|
||||||
|
* @param slotPlayerMap the map associating each slot with the player occupying it.
|
||||||
|
* @param orderLogicCard the order logic card that determines the player order.
|
||||||
|
* @param currentState the current state of the game.
|
||||||
|
* @param players the list of players in the game.
|
||||||
|
*/
|
||||||
public ApplyNextRound(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, List<Player> players){
|
public ApplyNextRound(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, List<Player> players){
|
||||||
super("SERVER",EventType.NEXT_ROUND,false);
|
super("SERVER",EventType.NEXT_ROUND,false);
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
|
|||||||
@@ -21,7 +21,15 @@ import java.util.Map;
|
|||||||
public class EndedGame extends NetworkEvent implements Serializable{
|
public class EndedGame extends NetworkEvent implements Serializable{
|
||||||
|
|
||||||
ArrayList<Player> players;
|
ArrayList<Player> players;
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Constructs an event containing the final game state.
|
||||||
|
*
|
||||||
|
* @param slotPlayerMap the map associating each slot with the player occupying it.
|
||||||
|
* @param orderLogicCard the order logic card that determines the player order.
|
||||||
|
* @param currentState the current state of the game.
|
||||||
|
* @param players the list of players at the end of the game.
|
||||||
|
*/
|
||||||
public EndedGame(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, ArrayList<Player> players){
|
public EndedGame(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, ArrayList<Player> players){
|
||||||
super("SERVER",EventType.ENDED_GAME,false);
|
super("SERVER",EventType.ENDED_GAME,false);
|
||||||
this.slotPlayerMap = slotPlayerMap;
|
this.slotPlayerMap = slotPlayerMap;
|
||||||
|
|||||||
@@ -7,39 +7,54 @@ import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Network event used to notify that a player has reconnected to the game.
|
||||||
|
*/
|
||||||
public class ReconnectPlayer extends NetworkEvent implements Serializable {
|
public class ReconnectPlayer extends NetworkEvent implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Constructs a reconnection event for the specified player.
|
||||||
* Initializes all the attributes.
|
*
|
||||||
* @param username the name of the player requesting the event
|
* @param username the username of the player who reconnected.
|
||||||
*/
|
*/
|
||||||
public ReconnectPlayer(String username) {
|
public ReconnectPlayer(String username) {
|
||||||
super(username, EventType.RECONNECT_PLAYER, false);
|
super(username, EventType.RECONNECT_PLAYER, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param gameController the Game Controller on which to apply the event
|
* Applies the reconnection event to the server-side game controller.
|
||||||
* @return true if the player could skipTheTurn, false otherwise
|
*
|
||||||
|
* @param gameController the game controller on which to apply the event.
|
||||||
|
* @return {@code true} if the player reconnection is handled successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(GameController gameController) {
|
public boolean apply(GameController gameController) {
|
||||||
return gameController.ReconnectPlayer(username);
|
return gameController.ReconnectPlayer(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Applies the reconnection update to the client-side mini model.
|
||||||
|
*
|
||||||
|
* <p>If the event does not represent an error, the player data,
|
||||||
|
* turn order, current game state, and slot assignments are updated.
|
||||||
|
*
|
||||||
|
* @param miniModel the mini model on which to apply the event.
|
||||||
|
* @return {@code true} if the update is applied successfully,
|
||||||
|
* {@code false} if the event represents an error.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(MiniModel miniModel) {
|
public boolean apply(MiniModel miniModel) {
|
||||||
if (isError)
|
if (isError)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (miniModel != null) {
|
if (miniModel != null) {
|
||||||
miniModel.setPlayer(player);
|
miniModel.setPlayer(player);
|
||||||
miniModel.setOrderLogicCard(orderLogicCard);
|
miniModel.setOrderLogicCard(orderLogicCard);
|
||||||
miniModel.setCurrentState(currentState);
|
miniModel.setCurrentState(currentState);
|
||||||
miniModel.setSlotPlayerMap(slotPlayerMap);
|
miniModel.setSlotPlayerMap(slotPlayerMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,49 +5,78 @@ import it.polimi.ingsw.gc14.Model.MiniModel;
|
|||||||
import it.polimi.ingsw.gc14.Model.Totems;
|
import it.polimi.ingsw.gc14.Model.Totems;
|
||||||
import it.polimi.ingsw.gc14.Network.EventType;
|
import it.polimi.ingsw.gc14.Network.EventType;
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||||
import it.polimi.ingsw.gc14.View.IView;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NetworkEvent to select a slot where to place the player totem.
|
* Network event used to handle a player's totem choice.
|
||||||
*/
|
*/
|
||||||
public class TotemChoice extends NetworkEvent implements Serializable {
|
public class TotemChoice extends NetworkEvent implements Serializable {
|
||||||
|
|
||||||
/** Index of the card to draw */
|
/**
|
||||||
|
* Name of the totem selected by the player.
|
||||||
|
*/
|
||||||
private String totem;
|
private String totem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of totems still available after the choice has been processed.
|
||||||
|
*/
|
||||||
private List<Totems> availableTotems;
|
private List<Totems> availableTotems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of currently available totems.
|
||||||
|
*
|
||||||
|
* @param availableTotems the totems still available for selection.
|
||||||
|
*/
|
||||||
public void setAvailableTotems(List<Totems> availableTotems) {
|
public void setAvailableTotems(List<Totems> availableTotems) {
|
||||||
this.availableTotems = availableTotems;
|
this.availableTotems = availableTotems;
|
||||||
}
|
}
|
||||||
//TODO
|
|
||||||
|
/**
|
||||||
|
* Constructs a totem choice event for the specified player.
|
||||||
|
*
|
||||||
|
* @param username the username of the player making the choice.
|
||||||
|
* @param totem the name of the selected totem.
|
||||||
|
*/
|
||||||
public TotemChoice(String username, String totem) {
|
public TotemChoice(String username, String totem) {
|
||||||
super(username, EventType.TOTEM_CHOICE, false);
|
super(username, EventType.TOTEM_CHOICE, false);
|
||||||
this.totem = totem;
|
this.totem = totem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param gameController the Game Controller on which to apply the event
|
* Applies the totem choice event to the server-side game controller.
|
||||||
* @return true if the player could draw the card, false otherwise
|
*
|
||||||
|
* @param gameController the game controller on which to apply the event.
|
||||||
|
* @return {@code true} if the totem choice is handled successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(GameController gameController) {
|
public boolean apply(GameController gameController) {
|
||||||
return gameController.TotemChoice(username, totem);
|
return gameController.TotemChoice(username, totem);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO
|
/**
|
||||||
|
* Applies the totem choice update to the client-side mini model.
|
||||||
|
*
|
||||||
|
* <p>If the event does not represent an error, the player data,
|
||||||
|
* turn order, current game state, slot assignments, and available
|
||||||
|
* totems are updated.
|
||||||
|
*
|
||||||
|
* @param miniModel the mini model on which to apply the event.
|
||||||
|
* @return {@code true} if the update is applied successfully,
|
||||||
|
* {@code false} if the event represents an error.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(MiniModel miniModel) {
|
public boolean apply(MiniModel miniModel) {
|
||||||
if (isError)
|
if (isError)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
miniModel.setPlayer(player);
|
miniModel.setPlayer(player);
|
||||||
miniModel.setOrderLogicCard(orderLogicCard);
|
miniModel.setOrderLogicCard(orderLogicCard);
|
||||||
miniModel.setCurrentState(currentState);
|
miniModel.setCurrentState(currentState);
|
||||||
miniModel.setSlotPlayerMap(slotPlayerMap);
|
miniModel.setSlotPlayerMap(slotPlayerMap);
|
||||||
miniModel.setAvailableTotems(availableTotems);
|
miniModel.setAvailableTotems(availableTotems);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
package it.polimi.ingsw.gc14.Network.RMI.Client;
|
package it.polimi.ingsw.gc14.Network.RMI.Client;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.rmi.RemoteException;
|
import java.rmi.RemoteException;
|
||||||
import java.rmi.registry.LocateRegistry;
|
import java.rmi.registry.LocateRegistry;
|
||||||
import java.rmi.registry.Registry;
|
import java.rmi.registry.Registry;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||||
import it.polimi.ingsw.gc14.Network.IClient;
|
import it.polimi.ingsw.gc14.Network.IClient;
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
|
import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
|
||||||
import it.polimi.ingsw.gc14.Network.RMI.Common.IClientCallback;
|
|
||||||
import it.polimi.ingsw.gc14.Network.RMI.Common.IGameServer;
|
import it.polimi.ingsw.gc14.Network.RMI.Common.IGameServer;
|
||||||
import it.polimi.ingsw.gc14.Network.RMI.Server.RMIServer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client RMI. Uses the methods exposed by the server RMI.
|
* Client RMI. Uses the methods exposed by the server RMI.
|
||||||
@@ -34,7 +29,14 @@ public class RMIClient implements IClient {
|
|||||||
/** Scheduler that fires ping() every PING_INTERVAL_S seconds. */
|
/** Scheduler that fires ping() every PING_INTERVAL_S seconds. */
|
||||||
private ScheduledExecutorService pingSender;
|
private ScheduledExecutorService pingSender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an RMI client and initializes its connection parameters.
|
||||||
|
*
|
||||||
|
* @param controller the client controller associated with this RMI client.
|
||||||
|
* @param host the hostname or IP address of the RMI server.
|
||||||
|
* @param port the port used to connect to the RMI registry.
|
||||||
|
* @param myIP the IP address of the client.
|
||||||
|
*/
|
||||||
public RMIClient(ClientController controller, String host, int port, String myIP) {
|
public RMIClient(ClientController controller, String host, int port, String myIP) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public interface IClientCallback extends Remote, Serializable {
|
|||||||
/**
|
/**
|
||||||
* Notifies the client that the game model has been initialized or updated.
|
* Notifies the client that the game model has been initialized or updated.
|
||||||
*
|
*
|
||||||
* @param model the current game model.
|
* @param miniModel the current mini model of the game.
|
||||||
* @throws RemoteException if an RMI communication error occurs.
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
*/
|
*/
|
||||||
void onGameInit(MiniModel miniModel) throws RemoteException;
|
void onGameInit(MiniModel miniModel) throws RemoteException;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package it.polimi.ingsw.gc14.Network.RMI.Common;
|
package it.polimi.ingsw.gc14.Network.RMI.Common;
|
||||||
|
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
|
||||||
|
|
||||||
import java.rmi.*;
|
import java.rmi.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,22 +7,89 @@ import java.rmi.*;
|
|||||||
*/
|
*/
|
||||||
public interface IGameServer extends Remote {
|
public interface IGameServer extends Remote {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a player to the game and registers the callback used by the server
|
||||||
|
* to notify the corresponding RMI client.
|
||||||
|
*
|
||||||
|
* @param username the username chosen by the player.
|
||||||
|
* @param preferredInt the preferred number of players for the game.
|
||||||
|
* @param callback the remote callback associated with the client.
|
||||||
|
* @return {@code true} if the player joins the game successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
boolean joinGame(String username, int preferredInt, IClientCallback callback) throws RemoteException;
|
boolean joinGame(String username, int preferredInt, IClientCallback callback) throws RemoteException;
|
||||||
|
|
||||||
boolean doEvent(NetworkEvent event) throws RemoteException;
|
|
||||||
|
|
||||||
void drawUpperTribeCard(String playerUsername, int pos) throws RemoteException;
|
|
||||||
void drawLowerTribeCard(String playerUsername, int pos) throws RemoteException;
|
|
||||||
void drawUpperBuildingCard(String playerUsername, int pos) throws RemoteException;
|
|
||||||
void drawLowerBuildingCard(String playerUsername, int pos) throws RemoteException;
|
|
||||||
void skipTurn(String playerUsername) throws RemoteException;
|
|
||||||
void slotChoice(String playerUsername, int pos) throws RemoteException;
|
|
||||||
void totemChoice(String playerUsername, String totems) throws RemoteException;
|
|
||||||
/**
|
/**
|
||||||
* Heartbeat: called periodically by the client to signal it is still alive.
|
* Requests to draw a tribe card from the upper tribe card list.
|
||||||
* Mirrors the PING/PONG mechanism used in the TCP heartbeat channel.
|
|
||||||
*
|
*
|
||||||
* @param username the username of the client sending the ping.
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void drawUpperTribeCard(String playerUsername, int pos) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to draw a tribe card from the lower tribe card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void drawLowerTribeCard(String playerUsername, int pos) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to draw a building card from the upper building card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void drawUpperBuildingCard(String playerUsername, int pos) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to draw a building card from the lower building card list.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player performing the action.
|
||||||
|
* @param pos the position of the selected card.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void drawLowerBuildingCard(String playerUsername, int pos) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to skip the current player's optional action.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player skipping the action.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void skipTurn(String playerUsername) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to assign the specified player to a slot.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the slot choice.
|
||||||
|
* @param pos the position of the selected slot.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void slotChoice(String playerUsername, int pos) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests to assign the selected totem to the specified player.
|
||||||
|
*
|
||||||
|
* @param playerUsername the username of the player making the totem choice.
|
||||||
|
* @param totems the name of the selected totem.
|
||||||
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
|
*/
|
||||||
|
void totemChoice(String playerUsername, String totems) throws RemoteException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat method called periodically by the client to signal
|
||||||
|
* that it is still connected.
|
||||||
|
*
|
||||||
|
* <p>This method mirrors the PING/PONG mechanism used in the TCP
|
||||||
|
* heartbeat channel.
|
||||||
|
*
|
||||||
|
* @param username the username of the client sending the heartbeat ping.
|
||||||
* @throws RemoteException if an RMI communication error occurs.
|
* @throws RemoteException if an RMI communication error occurs.
|
||||||
*/
|
*/
|
||||||
void ping(String username) throws RemoteException;
|
void ping(String username) throws RemoteException;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package it.polimi.ingsw.gc14.Network.RMI.Server;
|
package it.polimi.ingsw.gc14.Network.RMI.Server;
|
||||||
|
|
||||||
import it.polimi.ingsw.gc14.LimitedMap;
|
import it.polimi.ingsw.gc14.LimitedMap;
|
||||||
import it.polimi.ingsw.gc14.Model.Game;
|
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
|
import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
|
||||||
|
|
||||||
@@ -15,14 +14,13 @@ import java.util.concurrent.*;
|
|||||||
* instead of reading raw bytes from a dedicated socket, it relies on {@link #receivePing()}
|
* instead of reading raw bytes from a dedicated socket, it relies on {@link #receivePing()}
|
||||||
* being called by {@link RMIServer#ping(String)} every time the client sends a ping.
|
* being called by {@link RMIServer#ping(String)} every time the client sends a ping.
|
||||||
*
|
*
|
||||||
* <p>If no ping is received within {@value SILENCE_THRESHOLD_MS} ms the player is
|
* <p>If no ping is received within {@value SILENCE_THRESHOLD_MS} ms, the player is
|
||||||
* considered disconnected and {@link #disconnect()} is invoked, which:
|
* considered disconnected and {@link #disconnect()} is invoked, which:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>stops the watchdog;</li>
|
* <li>stops the watchdog;</li>
|
||||||
* <li>marks the player as offline in {@code playerList};</li>
|
* <li>marks the player as offline in {@code playerList};</li>
|
||||||
* <li>removes the callback from {@code clients};</li>
|
* <li>removes the callback from {@code clients};</li>
|
||||||
* <li>optionally pushes a {@link DisconnectedPlayer} event if it was that
|
* <li>adds a {@link DisconnectedPlayer} event to the action queue.</li>
|
||||||
* player's turn.</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class RMIHeartbeat {
|
public class RMIHeartbeat {
|
||||||
@@ -38,9 +36,6 @@ public class RMIHeartbeat {
|
|||||||
private volatile long lastPingTime = System.currentTimeMillis();
|
private volatile long lastPingTime = System.currentTimeMillis();
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
|
|
||||||
/** Reference to the current game model — needed to check whose turn it is. */
|
|
||||||
private volatile Game game;
|
|
||||||
|
|
||||||
private final ScheduledExecutorService watchdog =
|
private final ScheduledExecutorService watchdog =
|
||||||
Executors.newSingleThreadScheduledExecutor(r -> {
|
Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
Thread t = new Thread(r, "rmi-watchdog-" + username);
|
Thread t = new Thread(r, "rmi-watchdog-" + username);
|
||||||
@@ -48,6 +43,14 @@ public class RMIHeartbeat {
|
|||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an RMI heartbeat handler for the specified client.
|
||||||
|
*
|
||||||
|
* @param username the username of the client monitored by the heartbeat.
|
||||||
|
* @param playerList the map storing the connection status of the players.
|
||||||
|
* @param clients the collection of currently registered RMI clients.
|
||||||
|
* @param actionQueue the queue containing network events to be processed.
|
||||||
|
*/
|
||||||
public RMIHeartbeat(
|
public RMIHeartbeat(
|
||||||
String username,
|
String username,
|
||||||
LimitedMap<String, Boolean> playerList,
|
LimitedMap<String, Boolean> playerList,
|
||||||
@@ -80,24 +83,19 @@ public class RMIHeartbeat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the server to keep the watchdog up-to-date with the current game
|
* Disconnects the monitored RMI client.
|
||||||
* model (needed to check whose turn it is on disconnect).
|
*
|
||||||
|
* <p>The heartbeat handler is stopped, the player is marked as offline,
|
||||||
|
* the associated RMI callback is removed, and a disconnection event is
|
||||||
|
* added to the action queue for server-side processing.
|
||||||
*/
|
*/
|
||||||
public void setGame(Game game) {
|
|
||||||
this.game = game;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
running = false;
|
running = false;
|
||||||
watchdog.shutdownNow();
|
watchdog.shutdownNow();
|
||||||
|
|
||||||
// Mark player as offline
|
|
||||||
playerList.put(username, false);
|
playerList.put(username, false);
|
||||||
|
|
||||||
// Remove RMI callback so notifyAll skips this client
|
|
||||||
clients.remove(username);
|
clients.remove(username);
|
||||||
actionQueue.add(new DisconnectedPlayer(username));
|
actionQueue.add(new DisconnectedPlayer(username));
|
||||||
System.out.println("RMI disconnected: " + username);
|
System.out.println("RMI disconnected: " + username);
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ import java.rmi.*;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server RMI. Exposes a method to join the game and one to execute an event.
|
* RMI server responsible for handling remote client connections,
|
||||||
|
* receiving player actions, and propagating game updates.
|
||||||
|
*
|
||||||
|
* <p>The server manages player registration, reconnection handling,
|
||||||
|
* heartbeat monitoring, and the forwarding of client requests to the
|
||||||
|
* shared network event queue.
|
||||||
*/
|
*/
|
||||||
public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||||
private String host;
|
private String host;
|
||||||
@@ -43,10 +48,26 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
private LimitedMap<String, Boolean> playerList;
|
private LimitedMap<String, Boolean> playerList;
|
||||||
|
|
||||||
private boolean serverCrashed;
|
private boolean serverCrashed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the server is recovering from a previous crash.
|
||||||
|
*
|
||||||
|
* @param serverCrashed {@code true} if the server is in crash-recovery mode,
|
||||||
|
*/
|
||||||
public void setServerCrashed(boolean serverCrashed) {
|
public void setServerCrashed(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an RMI server with the required game and network components.
|
||||||
|
*
|
||||||
|
* @param controller the game controller used to manage the game logic.
|
||||||
|
* @param nPort the port used by the RMI registry.
|
||||||
|
* @param actionQueue the queue containing incoming network events.
|
||||||
|
* @param playerList the map storing the connection status of the players.
|
||||||
|
* @param host the hostname or IP address exposed by the RMI server.
|
||||||
|
* @throws RemoteException if the remote object cannot be exported.
|
||||||
|
*/
|
||||||
public RMIServer(GameController controller, int nPort,
|
public RMIServer(GameController controller, int nPort,
|
||||||
BlockingQueue<NetworkEvent> actionQueue,
|
BlockingQueue<NetworkEvent> actionQueue,
|
||||||
LimitedMap<String, Boolean> playerList,
|
LimitedMap<String, Boolean> playerList,
|
||||||
@@ -148,9 +169,13 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies all clients of a new event.
|
* Notifies connected RMI clients of a new network event.
|
||||||
* Also updates every watchdog with the latest model so disconnect logic
|
*
|
||||||
* knows whose turn it is.
|
* <p>If the event does not represent an error, it is sent to all registered clients.
|
||||||
|
* If it represents an error, it is sent only to the client that requested the action.
|
||||||
|
* Communication errors are logged without interrupting the notification process.
|
||||||
|
*
|
||||||
|
* @param action the network event to send to the clients.
|
||||||
*/
|
*/
|
||||||
public void notifyAll(NetworkEvent action){
|
public void notifyAll(NetworkEvent action){
|
||||||
for (Map.Entry<String, IClientCallback> entry : clients.entrySet()) {
|
for (Map.Entry<String, IClientCallback> entry : clients.entrySet()) {
|
||||||
@@ -168,15 +193,15 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies all clients of a new game model and keeps watchdogs up-to-date.
|
* Notifies all connected RMI clients of a new game model.
|
||||||
* Mirrors {@code TCPServer.notifyAll(Game)} + the {@code ClientHandler.notifyModel}
|
*
|
||||||
* call that stores the model for disconnect-turn checking.
|
* <p>The updated mini model is sent to each registered client callback.
|
||||||
|
* If a communication error occurs for a client, the exception is logged
|
||||||
|
* without interrupting the notification of the remaining clients.
|
||||||
|
*
|
||||||
|
* @param model the updated mini model to send to the connected clients.
|
||||||
*/
|
*/
|
||||||
public void notifyAll(MiniModel model) {
|
public void notifyAll(MiniModel model) {
|
||||||
// Keep every watchdog's game reference up to date
|
|
||||||
synchronized (controller){
|
|
||||||
watchdogs.values().forEach(wd -> wd.setGame(controller.getModel()));
|
|
||||||
}
|
|
||||||
for (IClientCallback cb : clients.values()) {
|
for (IClientCallback cb : clients.values()) {
|
||||||
try{
|
try{
|
||||||
cb.onGameInit(model);
|
cb.onGameInit(model);
|
||||||
@@ -188,17 +213,6 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push an action in actionQueue.
|
|
||||||
* @param action The desired actio
|
|
||||||
* @return true if the action was successfully added, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean doEvent(NetworkEvent action) {
|
|
||||||
return actionQueue.offer(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests to draw a tribe card from the upper list.
|
* Requests to draw a tribe card from the upper list.
|
||||||
* Creates a NetworkEvent and sends it through the network client.
|
* Creates a NetworkEvent and sends it through the network client.
|
||||||
@@ -269,10 +283,17 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Metodi per avviare server RMI
|
|
||||||
/**
|
/**
|
||||||
* Starts the RMI server.
|
* Starts the RMI server and binds it to the configured registry.
|
||||||
* @return true if the server starts successfully, false otherwise
|
*
|
||||||
|
* <p>The method configures the server hostname, creates the RMI registry
|
||||||
|
* on the specified port, and registers this server instance under the
|
||||||
|
* {@code RMIGameServer} name.
|
||||||
|
*
|
||||||
|
* @param serverCrashed {@code true} if the server is being restarted after a crash,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
* @return {@code true} if the server starts successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean start(boolean serverCrashed) {
|
public boolean start(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
@@ -288,12 +309,21 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the RMI server and removes its binding from the registry.
|
||||||
|
*
|
||||||
|
* <p>The remote server object is unexported, the registry entry associated
|
||||||
|
* with {@code RMIGameServer} is removed, and the server shutdown is logged.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the server is stopped successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
public boolean stop() {
|
public boolean stop() {
|
||||||
try {
|
try {
|
||||||
registry.unbind("RMIGameServer");
|
registry.unbind("RMIGameServer");
|
||||||
UnicastRemoteObject.unexportObject(this, true);
|
UnicastRemoteObject.unexportObject(this, true);
|
||||||
watchdogs.values().forEach(wd -> { /* watchdogs shut themselves down */ });
|
watchdogs.values().forEach(wd -> { /* watchdogs shut themselves down */ });
|
||||||
System.out.println("RMI Server fermato");
|
System.out.println("RMI Server stopped");
|
||||||
return true;
|
return true;
|
||||||
} catch (RemoteException | NotBoundException e) {
|
} catch (RemoteException | NotBoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -303,17 +333,13 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and starts a {@link RMIHeartbeat} for {@code username}.
|
* Creates and starts an {@link RMIHeartbeat} for the specified player.
|
||||||
* Also seeds the watchdog with the current model if one already exists
|
*
|
||||||
* (reconnection case).
|
* @param username the username of the player monitored by the heartbeat watchdog.
|
||||||
*/
|
*/
|
||||||
private void startWatchdog(String username) {
|
private void startWatchdog(String username) {
|
||||||
RMIHeartbeat wd = new RMIHeartbeat(
|
RMIHeartbeat wd = new RMIHeartbeat(
|
||||||
username, playerList, clients, actionQueue);
|
username, playerList, clients, actionQueue);
|
||||||
synchronized (controller)
|
|
||||||
{
|
|
||||||
if (controller.getModel() != null) wd.setGame(controller.getModel());
|
|
||||||
}
|
|
||||||
watchdogs.put(username, wd);
|
watchdogs.put(username, wd);
|
||||||
wd.start();
|
wd.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,12 @@ public class TCPClient implements IClient {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor that initializes the attributes.
|
* Constructs a TCP client and initializes its connection parameters.
|
||||||
* @param controller The game controller
|
*
|
||||||
* @param hostname The IP address of the server
|
* @param controller the client controller.
|
||||||
* @param port The TCP port of the server
|
* @param hostname the IP address or hostname of the server.
|
||||||
|
* @param mainPort the main TCP port of the server.
|
||||||
|
* @param heartbeatPort the TCP port used for heartbeat communication.
|
||||||
*/
|
*/
|
||||||
public TCPClient(ClientController controller, String hostname, int mainPort,int heartbeatPort ) {
|
public TCPClient(ClientController controller, String hostname, int mainPort,int heartbeatPort ) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public class ClientHandler implements Runnable {
|
|||||||
* @param out the output stream used to send data to the client.
|
* @param out the output stream used to send data to the client.
|
||||||
* @param in the input stream used to receive data from the client.
|
* @param in the input stream used to receive data from the client.
|
||||||
* @param clientHandlers the shared list of all active client handlers.
|
* @param clientHandlers the shared list of all active client handlers.
|
||||||
|
* @param playersMap the shared map containing the connected players.
|
||||||
* @param actionQueue the queue containing incoming events.
|
* @param actionQueue the queue containing incoming events.
|
||||||
*/
|
*/
|
||||||
public ClientHandler(String username, Socket clientSocket, ObjectOutputStream out, ObjectInputStream in, List<ClientHandler> clientHandlers, LimitedMap<String,Boolean> playersMap, BlockingQueue<NetworkEvent> actionQueue) {
|
public ClientHandler(String username, Socket clientSocket, ObjectOutputStream out, ObjectInputStream in, List<ClientHandler> clientHandlers, LimitedMap<String,Boolean> playersMap, BlockingQueue<NetworkEvent> actionQueue) {
|
||||||
@@ -130,6 +131,13 @@ public class ClientHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the client from the server.
|
||||||
|
*
|
||||||
|
* <p>The client handler is stopped and removed from the list of active handlers.
|
||||||
|
* The player is marked as disconnected, a {@link DisconnectedPlayer} event is
|
||||||
|
* added to the action queue, and the associated socket is closed.
|
||||||
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
running = false;
|
running = false;
|
||||||
clientHandlers.remove(this);
|
clientHandlers.remove(this);
|
||||||
|
|||||||
@@ -4,12 +4,26 @@ import java.io.*;
|
|||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.SynchronousQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the heartbeat communication associated with a TCP client.
|
||||||
|
*
|
||||||
|
* <p>This component listens for heartbeat {@code PING} messages from the client,
|
||||||
|
* replies with {@code PONG} messages, and periodically checks whether the client
|
||||||
|
* has remained silent for too long. If a timeout or communication error occurs,
|
||||||
|
* both the heartbeat connection and the main client connection are disconnected.
|
||||||
|
*/
|
||||||
public class HeartbeatHandler implements Runnable {
|
public class HeartbeatHandler implements Runnable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed time without receiving heartbeat messages before disconnecting the client.
|
||||||
|
*/
|
||||||
private static final long SILENCE_THRESHOLD_MS = 5_000;
|
private static final long SILENCE_THRESHOLD_MS = 5_000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected interval between heartbeat messages.
|
||||||
|
*/
|
||||||
private static final long KEEPALIVE_INTERVAL_MS = 3_000;
|
private static final long KEEPALIVE_INTERVAL_MS = 3_000;
|
||||||
|
|
||||||
private final String username;
|
private final String username;
|
||||||
@@ -17,18 +31,46 @@ public class HeartbeatHandler implements Runnable {
|
|||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final OutputStream out;
|
private final OutputStream out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Byte value representing a heartbeat ping message.
|
||||||
|
*/
|
||||||
private static final int PING = 1;
|
private static final int PING = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Byte value representing a heartbeat pong response.
|
||||||
|
*/
|
||||||
private static final int PONG = 2;
|
private static final int PONG = 2;
|
||||||
|
|
||||||
// riferimento al ClientHandler principale per disconnetterlo insieme
|
/**
|
||||||
|
* Main client handler associated with this heartbeat connection.
|
||||||
|
*/
|
||||||
private final ClientHandler mainHandler;
|
private final ClientHandler mainHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of the last received heartbeat message.
|
||||||
|
*/
|
||||||
private volatile long lastReceivedTime = System.currentTimeMillis();
|
private volatile long lastReceivedTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the heartbeat handler is still active.
|
||||||
|
*/
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executor used to periodically check heartbeat timeouts.
|
||||||
|
*/
|
||||||
private final ScheduledExecutorService watchdog =
|
private final ScheduledExecutorService watchdog =
|
||||||
Executors.newSingleThreadScheduledExecutor();
|
Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a heartbeat handler for the specified client.
|
||||||
|
*
|
||||||
|
* @param username the username of the connected client.
|
||||||
|
* @param socket the socket dedicated to heartbeat communication.
|
||||||
|
* @param mainHandler the main client handler associated with the same player.
|
||||||
|
* @throws IOException if the input or output stream cannot be obtained from the socket.
|
||||||
|
*/
|
||||||
public HeartbeatHandler(String username, Socket socket, ClientHandler mainHandler)
|
public HeartbeatHandler(String username, Socket socket, ClientHandler mainHandler)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
@@ -38,6 +80,14 @@ public class HeartbeatHandler implements Runnable {
|
|||||||
this.out = socket.getOutputStream();
|
this.out = socket.getOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the heartbeat listening loop.
|
||||||
|
*
|
||||||
|
* <p>The method activates the watchdog task, then waits for incoming heartbeat
|
||||||
|
* messages. When a {@code PING} is received, the last-received timestamp is
|
||||||
|
* updated and a {@code PONG} response is sent back to the client.
|
||||||
|
* If the stream is closed or an I/O error occurs, the client is disconnected.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
startWatchdog();
|
startWatchdog();
|
||||||
@@ -56,6 +106,12 @@ public class HeartbeatHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the periodic watchdog task that detects heartbeat timeouts.
|
||||||
|
*
|
||||||
|
* <p>If no heartbeat message is received within the configured silence threshold,
|
||||||
|
* the associated client is disconnected.
|
||||||
|
*/
|
||||||
private void startWatchdog() {
|
private void startWatchdog() {
|
||||||
watchdog.scheduleAtFixedRate(() -> {
|
watchdog.scheduleAtFixedRate(() -> {
|
||||||
if (System.currentTimeMillis() - lastReceivedTime > SILENCE_THRESHOLD_MS) {
|
if (System.currentTimeMillis() - lastReceivedTime > SILENCE_THRESHOLD_MS) {
|
||||||
@@ -65,7 +121,12 @@ public class HeartbeatHandler implements Runnable {
|
|||||||
}, 1, 1, TimeUnit.SECONDS);
|
}, 1, 1, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the heartbeat channel and the associated main client connection.
|
||||||
|
*
|
||||||
|
* <p>The handler is stopped, the watchdog task is terminated, the main
|
||||||
|
* {@link ClientHandler} is disconnected, and the heartbeat socket is closed.
|
||||||
|
*/
|
||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
running = false;
|
running = false;
|
||||||
watchdog.shutdownNow();
|
watchdog.shutdownNow();
|
||||||
|
|||||||
@@ -18,33 +18,96 @@ import java.util.concurrent.BlockingQueue;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server TCP. Accepts connections and manages all client handlers.
|
* TCP server responsible for accepting client connections,
|
||||||
|
* handling player registration and reconnection, and sending
|
||||||
|
* game updates to connected clients.
|
||||||
|
*
|
||||||
|
* <p>The server also manages a dedicated heartbeat channel used
|
||||||
|
* to detect disconnected clients and associate each heartbeat
|
||||||
|
* connection with the corresponding {@link ClientHandler}.
|
||||||
*/
|
*/
|
||||||
public class TCPServer {
|
public class TCPServer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main TCP port used for standard client-server communication.
|
||||||
|
*/
|
||||||
int port;
|
int port;
|
||||||
int heartbeatPort; // ← nuova porta
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP port dedicated to heartbeat communication.
|
||||||
|
*/
|
||||||
|
int heartbeatPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of players that have successfully connected.
|
||||||
|
*/
|
||||||
int connectedPlayers;
|
int connectedPlayers;
|
||||||
ServerSocket socketTCP;
|
|
||||||
ServerSocket heartbeatSocketTCP; // ← nuovo ServerSocket
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main server socket used to accept client connections.
|
||||||
|
*/
|
||||||
|
ServerSocket socketTCP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server socket used to accept heartbeat connections.
|
||||||
|
*/
|
||||||
|
ServerSocket heartbeatSocketTCP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game controller used to manage the server-side game logic.
|
||||||
|
*/
|
||||||
final GameController controller;
|
final GameController controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue containing network events received from clients.
|
||||||
|
*/
|
||||||
BlockingQueue<NetworkEvent> actionQueue;
|
BlockingQueue<NetworkEvent> actionQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map storing the online/offline status of connected players.
|
||||||
|
*/
|
||||||
LimitedMap<String, Boolean> playerList;
|
LimitedMap<String, Boolean> playerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of active TCP client handlers.
|
||||||
|
*/
|
||||||
List<ClientHandler> clientHandlers;
|
List<ClientHandler> clientHandlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating whether the server is recovering from a previous crash.
|
||||||
|
*/
|
||||||
boolean serverCrashed;
|
boolean serverCrashed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the server is recovering from a previous crash.
|
||||||
|
*
|
||||||
|
* @param serverCrashed {@code true} if the server is in crash-recovery mode,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
public void setServerCrashed(boolean serverCrashed) {
|
public void setServerCrashed(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mappa temporanea: username → ClientHandler
|
/**
|
||||||
// Serve per associare il socket heartbeat al giusto ClientHandler
|
* Temporary map associating each username with the corresponding
|
||||||
|
* {@link ClientHandler} waiting for its heartbeat connection.
|
||||||
|
*/
|
||||||
private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>();
|
private final Map<String, ClientHandler> pendingHeartbeat = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public TCPServer(GameController controller, int port, int heartbeatPort,
|
/**
|
||||||
BlockingQueue<NetworkEvent> actionQueue, LimitedMap<String, Boolean> playerList) {
|
* Constructs a TCP server with the required game and network components.
|
||||||
|
*
|
||||||
|
* @param controller the game controller used to manage the game logic.
|
||||||
|
* @param port the main TCP port used for client communication.
|
||||||
|
* @param heartbeatPort the TCP port dedicated to heartbeat connections.
|
||||||
|
* @param actionQueue the queue containing incoming network events.
|
||||||
|
* @param playerList the map storing the connection status of the players.
|
||||||
|
*/
|
||||||
|
public TCPServer(GameController controller,
|
||||||
|
int port,
|
||||||
|
int heartbeatPort,
|
||||||
|
BlockingQueue<NetworkEvent> actionQueue,
|
||||||
|
LimitedMap<String, Boolean> playerList) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.heartbeatPort = heartbeatPort;
|
this.heartbeatPort = heartbeatPort;
|
||||||
this.connectedPlayers = 0;
|
this.connectedPlayers = 0;
|
||||||
@@ -54,8 +117,24 @@ public class TCPServer {
|
|||||||
this.clientHandlers = new ArrayList<>();
|
this.clientHandlers = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the TCP server and begins accepting client connections.
|
||||||
|
*
|
||||||
|
* <p>The method opens both the main TCP server socket and the dedicated
|
||||||
|
* heartbeat server socket. It then starts a separate thread for heartbeat
|
||||||
|
* connections and continuously waits for new players or reconnecting clients.
|
||||||
|
*
|
||||||
|
* <p>When a new connection is received, the first event must be an
|
||||||
|
* {@link AddPlayer} request. Depending on the current server state,
|
||||||
|
* the connection is handled either as a new player joining the game
|
||||||
|
* or as a reconnection attempt.
|
||||||
|
*
|
||||||
|
* @param serverCrashed {@code true} if the server is being restarted after a crash,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
public void start(boolean serverCrashed) {
|
public void start(boolean serverCrashed) {
|
||||||
this.serverCrashed = serverCrashed;
|
this.serverCrashed = serverCrashed;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socketTCP = new ServerSocket(port);
|
socketTCP = new ServerSocket(port);
|
||||||
heartbeatSocketTCP = new ServerSocket(heartbeatPort);
|
heartbeatSocketTCP = new ServerSocket(heartbeatPort);
|
||||||
@@ -68,16 +147,19 @@ public class TCPServer {
|
|||||||
System.out.println("TCP server started on port: " + port);
|
System.out.println("TCP server started on port: " + port);
|
||||||
System.out.println("Heartbeat server started on port: " + heartbeatPort);
|
System.out.println("Heartbeat server started on port: " + heartbeatPort);
|
||||||
|
|
||||||
// Thread separato per accettare le connessioni heartbeat
|
|
||||||
new Thread(this::acceptHeartbeat, "heartbeat-acceptor").start();
|
new Thread(this::acceptHeartbeat, "heartbeat-acceptor").start();
|
||||||
|
|
||||||
// Loop principale — invariato nella logica, cambia solo la creazione del ClientHandler
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Socket clientSocket = socketTCP.accept();
|
Socket clientSocket = socketTCP.accept();
|
||||||
ObjectOutputStream clientSend = new ObjectOutputStream(clientSocket.getOutputStream());
|
|
||||||
ObjectInputStream clientReceive = new ObjectInputStream(clientSocket.getInputStream());
|
ObjectOutputStream clientSend =
|
||||||
|
new ObjectOutputStream(clientSocket.getOutputStream());
|
||||||
|
ObjectInputStream clientReceive =
|
||||||
|
new ObjectInputStream(clientSocket.getInputStream());
|
||||||
|
|
||||||
NetworkEvent event = (NetworkEvent) clientReceive.readObject();
|
NetworkEvent event = (NetworkEvent) clientReceive.readObject();
|
||||||
|
|
||||||
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
|
if (!(event.getEventType() == EventType.ADD_PLAYER)) {
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
@@ -87,7 +169,8 @@ public class TCPServer {
|
|||||||
|
|
||||||
AddPlayer eventAddPlayer = (AddPlayer) event;
|
AddPlayer eventAddPlayer = (AddPlayer) event;
|
||||||
|
|
||||||
if (eventAddPlayer.getProposedNPlayer() < 2 || eventAddPlayer.getProposedNPlayer() > 5) {
|
if (eventAddPlayer.getProposedNPlayer() < 2
|
||||||
|
|| eventAddPlayer.getProposedNPlayer() > 5) {
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
System.out.println("Invalid parameters. Connection terminated.");
|
System.out.println("Invalid parameters. Connection terminated.");
|
||||||
@@ -95,128 +178,186 @@ public class TCPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (controller) {
|
synchronized (controller) {
|
||||||
|
|
||||||
String username = eventAddPlayer.getUsername();
|
String username = eventAddPlayer.getUsername();
|
||||||
|
|
||||||
if (serverCrashed) {
|
if (serverCrashed) {
|
||||||
if(controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))&& !playerList.containsKey(username)){
|
if (controller.getModel().getPlayers().stream()
|
||||||
|
.anyMatch(p -> p.getUserName().equals(username))
|
||||||
|
&& !playerList.containsKey(username)) {
|
||||||
|
|
||||||
playerList.put(username, true);
|
playerList.put(username, true);
|
||||||
System.out.println("(After crash)Reconnected player: " + username);
|
System.out.println("(After crash)Reconnected player: " + username);
|
||||||
|
|
||||||
ClientHandler handler = new ClientHandler(
|
ClientHandler handler = new ClientHandler(
|
||||||
username, clientSocket, clientSend, clientReceive,
|
username,
|
||||||
clientHandlers,playerList, actionQueue
|
clientSocket,
|
||||||
|
clientSend,
|
||||||
|
clientReceive,
|
||||||
|
clientHandlers,
|
||||||
|
playerList,
|
||||||
|
actionQueue
|
||||||
);
|
);
|
||||||
|
|
||||||
clientSocket.getOutputStream().write(1);
|
clientSocket.getOutputStream().write(1);
|
||||||
pendingHeartbeat.put(username, handler);
|
pendingHeartbeat.put(username, handler);
|
||||||
|
|
||||||
Thread thread = new Thread(handler);
|
Thread thread = new Thread(handler);
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
clientHandlers.add(handler);
|
clientHandlers.add(handler);
|
||||||
connectedPlayers++;
|
connectedPlayers++;
|
||||||
}
|
|
||||||
else if(playerList.containsKey(username) && !playerList.get(username)){
|
} else if (playerList.containsKey(username)
|
||||||
// riconnessione
|
&& !playerList.get(username)) {
|
||||||
|
|
||||||
playerList.put(username, true);
|
playerList.put(username, true);
|
||||||
System.out.println("Reconnected player: " + username);
|
System.out.println("Reconnected player: " + username);
|
||||||
|
|
||||||
ClientHandler handler = new ClientHandler(
|
ClientHandler handler = new ClientHandler(
|
||||||
username, clientSocket, clientSend, clientReceive,
|
username,
|
||||||
clientHandlers, playerList, actionQueue
|
clientSocket,
|
||||||
|
clientSend,
|
||||||
|
clientReceive,
|
||||||
|
clientHandlers,
|
||||||
|
playerList,
|
||||||
|
actionQueue
|
||||||
);
|
);
|
||||||
|
|
||||||
clientSocket.getOutputStream().write(1);
|
clientSocket.getOutputStream().write(1);
|
||||||
pendingHeartbeat.put(username, handler);
|
pendingHeartbeat.put(username, handler);
|
||||||
|
|
||||||
Game game = controller.getModel();
|
Game game = controller.getModel();
|
||||||
handler.notifyMiniModel(new MiniModel(game.getBoard(),game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems()));
|
handler.notifyMiniModel(new MiniModel(
|
||||||
|
game.getBoard(),
|
||||||
|
game.getSlotMap(),
|
||||||
|
game.orderLogicCard,
|
||||||
|
game.getCurrentState(),
|
||||||
|
game.getPlayers(),
|
||||||
|
game.getAvailableTotems()
|
||||||
|
));
|
||||||
|
|
||||||
Thread thread = new Thread(handler);
|
Thread thread = new Thread(handler);
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
clientHandlers.add(handler);
|
clientHandlers.add(handler);
|
||||||
connectedPlayers++;
|
connectedPlayers++;
|
||||||
actionQueue.add(new ReconnectPlayer(username));
|
actionQueue.add(new ReconnectPlayer(username));
|
||||||
}
|
|
||||||
else
|
} else {
|
||||||
{
|
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
System.out.println("Player could not be added. Connection terminated.");
|
System.out.println("Player could not be added. Connection terminated.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
} else {
|
||||||
{
|
|
||||||
if (playerList.isEmpty()) {
|
if (playerList.isEmpty()) {
|
||||||
Game model = new Game(eventAddPlayer.getProposedNPlayer());
|
Game model = new Game(eventAddPlayer.getProposedNPlayer());
|
||||||
controller.setModel(model);
|
controller.setModel(model);
|
||||||
playerList.setLimit(eventAddPlayer.getProposedNPlayer());
|
playerList.setLimit(eventAddPlayer.getProposedNPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller.addPlayer(username)) {
|
if (controller.addPlayer(username)) {
|
||||||
// nuovo giocatore
|
|
||||||
playerList.put(username, true);
|
playerList.put(username, true);
|
||||||
System.out.println("Accepted player: " + username);
|
System.out.println("Accepted player: " + username);
|
||||||
|
|
||||||
ClientHandler handler = new ClientHandler(
|
ClientHandler handler = new ClientHandler(
|
||||||
username, clientSocket, clientSend, clientReceive,
|
username,
|
||||||
clientHandlers,playerList, actionQueue
|
clientSocket,
|
||||||
|
clientSend,
|
||||||
|
clientReceive,
|
||||||
|
clientHandlers,
|
||||||
|
playerList,
|
||||||
|
actionQueue
|
||||||
);
|
);
|
||||||
|
|
||||||
clientSocket.getOutputStream().write(1);
|
clientSocket.getOutputStream().write(1);
|
||||||
pendingHeartbeat.put(username, handler);
|
pendingHeartbeat.put(username, handler);
|
||||||
|
|
||||||
Thread thread = new Thread(handler);
|
Thread thread = new Thread(handler);
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
clientHandlers.add(handler);
|
clientHandlers.add(handler);
|
||||||
connectedPlayers++;
|
connectedPlayers++;
|
||||||
// metti in attesa del socket heartbeat
|
|
||||||
}
|
} else if (playerList.containsKey(username)
|
||||||
else if(playerList.containsKey(username) && !playerList.get(username)){
|
&& !playerList.get(username)) {
|
||||||
// riconnessione
|
|
||||||
playerList.put(username, true);
|
playerList.put(username, true);
|
||||||
System.out.println("Reconnected player: " + username);
|
System.out.println("Reconnected player: " + username);
|
||||||
|
|
||||||
ClientHandler handler = new ClientHandler(
|
ClientHandler handler = new ClientHandler(
|
||||||
username, clientSocket, clientSend, clientReceive,
|
username,
|
||||||
clientHandlers, playerList, actionQueue
|
clientSocket,
|
||||||
|
clientSend,
|
||||||
|
clientReceive,
|
||||||
|
clientHandlers,
|
||||||
|
playerList,
|
||||||
|
actionQueue
|
||||||
);
|
);
|
||||||
|
|
||||||
clientSocket.getOutputStream().write(1);
|
clientSocket.getOutputStream().write(1);
|
||||||
pendingHeartbeat.put(username, handler);
|
pendingHeartbeat.put(username, handler);
|
||||||
|
|
||||||
Game game = controller.getModel();
|
Game game = controller.getModel();
|
||||||
handler.notifyMiniModel(new MiniModel(game.getBoard(),game.getSlotMap(), game.orderLogicCard,game.getCurrentState(),game.getPlayers(),game.getAvailableTotems()));
|
handler.notifyMiniModel(new MiniModel(
|
||||||
|
game.getBoard(),
|
||||||
|
game.getSlotMap(),
|
||||||
|
game.orderLogicCard,
|
||||||
|
game.getCurrentState(),
|
||||||
|
game.getPlayers(),
|
||||||
|
game.getAvailableTotems()
|
||||||
|
));
|
||||||
|
|
||||||
Thread thread = new Thread(handler);
|
Thread thread = new Thread(handler);
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
clientHandlers.add(handler);
|
clientHandlers.add(handler);
|
||||||
connectedPlayers++;
|
connectedPlayers++;
|
||||||
actionQueue.add(new ReconnectPlayer(username));
|
actionQueue.add(new ReconnectPlayer(username));
|
||||||
}
|
|
||||||
else{
|
} else {
|
||||||
clientSocket.getOutputStream().write(-1);
|
clientSocket.getOutputStream().write(-1);
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
System.out.println("Player could not be added. Connection terminated.");
|
System.out.println("Player could not be added. Connection terminated.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
}
|
|
||||||
catch(IOException | ClassNotFoundException e){
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accetta connessioni sul socket heartbeat e le associa al ClientHandler giusto.
|
* Accepts heartbeat connections and associates them with the correct client handler.
|
||||||
* Il client manda subito il proprio username per identificarsi.
|
*
|
||||||
|
* <p>Each client immediately sends its username on the heartbeat channel.
|
||||||
|
* The method uses that username to retrieve the pending {@link ClientHandler}
|
||||||
|
* and starts a dedicated {@link HeartbeatHandler}. If no pending handler is found,
|
||||||
|
* the heartbeat socket is closed.
|
||||||
*/
|
*/
|
||||||
private void acceptHeartbeat() {
|
private void acceptHeartbeat() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Socket hbSocket = heartbeatSocketTCP.accept();
|
Socket hbSocket = heartbeatSocketTCP.accept();
|
||||||
ObjectInputStream hbIn = new ObjectInputStream(hbSocket.getInputStream());
|
ObjectInputStream hbIn =
|
||||||
|
new ObjectInputStream(hbSocket.getInputStream());
|
||||||
|
|
||||||
// il client manda subito il suo username
|
|
||||||
String username = (String) hbIn.readObject();
|
String username = (String) hbIn.readObject();
|
||||||
|
|
||||||
ClientHandler handler = pendingHeartbeat.remove(username);
|
ClientHandler handler = pendingHeartbeat.remove(username);
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
HeartbeatHandler hb = new HeartbeatHandler(username, hbSocket, handler);
|
HeartbeatHandler hb =
|
||||||
|
new HeartbeatHandler(username, hbSocket, handler);
|
||||||
|
|
||||||
new Thread(hb, "heartbeat-" + username).start();
|
new Thread(hb, "heartbeat-" + username).start();
|
||||||
System.out.println("Heartbeat connected for: " + username);
|
System.out.println("Heartbeat connected for: " + username);
|
||||||
} else {
|
} else {
|
||||||
System.out.println("No pending handler for: " + username + ", closing heartbeat.");
|
System.out.println(
|
||||||
|
"No pending handler for: " + username + ", closing heartbeat."
|
||||||
|
);
|
||||||
hbSocket.close();
|
hbSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,13 +367,27 @@ public class TCPServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies connected TCP clients of a new network event.
|
||||||
|
*
|
||||||
|
* <p>If the event does not represent an error, it is sent to all connected clients.
|
||||||
|
* If it represents an error, it is sent only to the client that requested the action.
|
||||||
|
*
|
||||||
|
* @param event the network event to send to the clients.
|
||||||
|
*/
|
||||||
public void notifyAll(NetworkEvent event) {
|
public void notifyAll(NetworkEvent event) {
|
||||||
clientHandlers.forEach(h -> {
|
clientHandlers.forEach(h -> {
|
||||||
if (!event.getIsError() || event.getUsername().equals(h.getUsername()))
|
if (!event.getIsError() || event.getUsername().equals(h.getUsername())) {
|
||||||
h.notifyEvent(event);
|
h.notifyEvent(event);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies all connected TCP clients of a new game model.
|
||||||
|
*
|
||||||
|
* @param model the updated mini model to send to the clients.
|
||||||
|
*/
|
||||||
public void notifyAll(MiniModel model) {
|
public void notifyAll(MiniModel model) {
|
||||||
clientHandlers.forEach(h -> h.notifyMiniModel(model));
|
clientHandlers.forEach(h -> h.notifyMiniModel(model));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,15 +192,26 @@ public class ServerLauncher {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first method executed when the server program is launched.
|
* Entry point of the server application.
|
||||||
* It creates all the objects needed: playerList, actionQueue, gameController, serverRMI, serverTCP, launcher.
|
|
||||||
* Then sets the playerList's action to execute launcher.run() and starts the TCP/RMI servers.
|
|
||||||
* Note: the model is initialized and set in the controller in TCP/RMI servers when the first user decides the number of players.
|
|
||||||
*
|
*
|
||||||
* @throws InterruptedException if this exception is issued by run method
|
* <p>The method initializes the shared player list, the network event queue,
|
||||||
* @throws RemoteException if this exception is issued by run method
|
* the game controller, the RMI server, the TCP server, and the server launcher.
|
||||||
|
* It also selects the network interface to expose, configures the server crash
|
||||||
|
* recovery state, and defines the action to execute once the required number
|
||||||
|
* of players has joined the game.
|
||||||
|
*
|
||||||
|
* <p>When the player list reaches its limit, the current game model is converted
|
||||||
|
* into a {@link MiniModel}, sent to all connected clients through both RMI and TCP,
|
||||||
|
* and rendered on the server-side TUI. Finally, the event-processing launcher
|
||||||
|
* and both network servers are started.
|
||||||
|
*
|
||||||
|
* <p>The game model itself is initialized by the TCP or RMI server when the first
|
||||||
|
* player joins and selects the total number of players.
|
||||||
|
*
|
||||||
|
* @param args the command-line arguments passed to the server application.
|
||||||
|
* @throws RemoteException if the RMI server cannot be created.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws InterruptedException, RemoteException {
|
public static void main(String[] args) throws RemoteException {
|
||||||
playerList = new LimitedMap<String,Boolean>(5, ()->{});
|
playerList = new LimitedMap<String,Boolean>(5, ()->{});
|
||||||
BlockingQueue<NetworkEvent> actionQueue = new LinkedBlockingQueue<>();
|
BlockingQueue<NetworkEvent> actionQueue = new LinkedBlockingQueue<>();
|
||||||
GameController gameController = new GameController();
|
GameController gameController = new GameController();
|
||||||
@@ -261,10 +272,13 @@ public class ServerLauncher {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and executes the game.
|
* Executes the main game loop.
|
||||||
* Game creation: TCP/RMI servers send the game model to all players.
|
*
|
||||||
* Game execution: repeatedly calls doFirstEvent() to process the events in the actionQueue.
|
* <p>The method repeatedly processes the first event in the action queue
|
||||||
* @throws InterruptedException if the TCP server thread is interrupted
|
* and, when a view is available, updates the rendered game state.
|
||||||
|
*
|
||||||
|
* <p>If the thread is interrupted while waiting for an event,
|
||||||
|
* the interruption status is restored and the loop terminates.
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -282,6 +296,18 @@ public class ServerLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets the user choose the network interface to be used by the server.
|
||||||
|
*
|
||||||
|
* <p>The method scans all active, non-loopback, and non-virtual network
|
||||||
|
* interfaces, collecting their IPv4 addresses. If only one valid address is
|
||||||
|
* found, it is selected automatically. Otherwise, the available addresses are
|
||||||
|
* printed and the user is asked to choose one by index.
|
||||||
|
*
|
||||||
|
* @param scanner the scanner used to read the user's selection.
|
||||||
|
* @return the IPv4 address of the selected network interface.
|
||||||
|
* @throws Exception if no valid network interface is available.
|
||||||
|
*/
|
||||||
public static String chooseNetworkInterface(Scanner scanner) throws Exception {
|
public static String chooseNetworkInterface(Scanner scanner) throws Exception {
|
||||||
List<String> ips = new ArrayList<>();
|
List<String> ips = new ArrayList<>();
|
||||||
|
|
||||||
@@ -315,6 +341,16 @@ public class ServerLauncher {
|
|||||||
return ips.get(choice);
|
return ips.get(choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current game model to a local file.
|
||||||
|
*
|
||||||
|
* <p>The save file is stored in the {@code GameSaves/save.dat} path relative
|
||||||
|
* to the server executable location. If the directory does not exist, it is
|
||||||
|
* created before writing the serialized game model.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the game is saved successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
private boolean gameSave(){
|
private boolean gameSave(){
|
||||||
try{
|
try{
|
||||||
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
||||||
@@ -340,6 +376,16 @@ public class ServerLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a previously saved game model from the local save file.
|
||||||
|
*
|
||||||
|
* <p>The method attempts to deserialize the game stored in
|
||||||
|
* {@code GameSaves/save.dat}. If no save file exists or an I/O error occurs,
|
||||||
|
* {@code null} is returned.
|
||||||
|
*
|
||||||
|
* @return the loaded {@link Game} instance, or {@code null} if no valid save
|
||||||
|
* can be loaded.
|
||||||
|
*/
|
||||||
private Game loadSave(){
|
private Game loadSave(){
|
||||||
try {
|
try {
|
||||||
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
||||||
@@ -363,6 +409,12 @@ public class ServerLauncher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the current local game save file.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the save file is deleted successfully,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
private boolean deleteSave(){
|
private boolean deleteSave(){
|
||||||
try{
|
try{
|
||||||
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
Path jarPath = Paths.get(getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import javafx.stage.Screen;
|
|||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class TotemFXMLController {
|
public class TotemFXMLController {
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,13 @@ public class TUI implements IView {
|
|||||||
System.out.println(AsciiTable.sideBySide(lines, lines2, 3));
|
System.out.println(AsciiTable.sideBySide(lines, lines2, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the available totems in the terminal interface.
|
||||||
|
*
|
||||||
|
* <p>The method clears the terminal, builds a table containing the currently
|
||||||
|
* available totems together with their selection positions, and displays the
|
||||||
|
* instructions required to choose one.
|
||||||
|
*/
|
||||||
public void renderTotems() {
|
public void renderTotems() {
|
||||||
clearTerminal();
|
clearTerminal();
|
||||||
var table = new AsciiTable(BorderStyle.ROUNDED, model.availableTotems.size());
|
var table = new AsciiTable(BorderStyle.ROUNDED, model.availableTotems.size());
|
||||||
|
|||||||
Reference in New Issue
Block a user