Fix: full network stack refactor
This commit is contained in:
@@ -10,7 +10,7 @@ public enum ErrorType {
|
||||
GENERIC_ERROR("Generic error"),
|
||||
GAME_ALREADY_STARTED("Game already started"),
|
||||
WRONG_ACTION("Wrong action");
|
||||
private String description;
|
||||
private final String description;
|
||||
ErrorType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ public class GameEventProcessor {
|
||||
event.setIsError(!event.apply(gameController));
|
||||
Game game = gameController.getModel();
|
||||
|
||||
if (event.getIsError()) {
|
||||
if (event.isError()) {
|
||||
broadcaster.notifyAll(event);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,10 +191,82 @@ public class MiniModel implements Serializable {
|
||||
*
|
||||
* @param standingPlayers the final ordered list of players.
|
||||
*/
|
||||
public void setStandingPlayers(ArrayList<Player> standingPlayers) {
|
||||
public void setStandingPlayers(List<Player> standingPlayers) {
|
||||
this.standingPlayers = standingPlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the upper row of tribe cards on the board.
|
||||
*
|
||||
* @param cards the new upper tribe card list.
|
||||
*/
|
||||
public void setUpperListTribeCards(ArrayList<TribeCard> cards) {
|
||||
this.upperListTribeCards = cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the lower row of tribe cards on the board.
|
||||
*
|
||||
* @param cards the new lower tribe card list.
|
||||
*/
|
||||
public void setLowerListTribeCards(ArrayList<TribeCard> cards) {
|
||||
this.lowerListTribeCards = cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the upper row of building cards on the board.
|
||||
*
|
||||
* @param cards the new upper building card list.
|
||||
*/
|
||||
public void setUpperListBuildingCards(ArrayList<BuildingCard> cards) {
|
||||
this.upperListBuildingCards = cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the lower row of building cards on the board.
|
||||
*
|
||||
* @param cards the new lower building card list.
|
||||
*/
|
||||
public void setLowerListBuildingCards(ArrayList<BuildingCard> cards) {
|
||||
this.lowerListBuildingCards = cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the card at the given position from the upper tribe card list.
|
||||
*
|
||||
* @param pos the zero-based index of the card to remove.
|
||||
*/
|
||||
public void removeUpperTribeCard(int pos) {
|
||||
if (upperListTribeCards != null) upperListTribeCards.remove(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the card at the given position from the lower tribe card list.
|
||||
*
|
||||
* @param pos the zero-based index of the card to remove.
|
||||
*/
|
||||
public void removeLowerTribeCard(int pos) {
|
||||
if (lowerListTribeCards != null) lowerListTribeCards.remove(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the card at the given position from the upper building card list.
|
||||
*
|
||||
* @param pos the zero-based index of the card to remove.
|
||||
*/
|
||||
public void removeUpperBuildingCard(int pos) {
|
||||
if (upperListBuildingCards != null) upperListBuildingCards.remove(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the card at the given position from the lower building card list.
|
||||
*
|
||||
* @param pos the zero-based index of the card to remove.
|
||||
*/
|
||||
public void removeLowerBuildingCard(int pos) {
|
||||
if (lowerListBuildingCards != null) lowerListBuildingCards.remove(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the player with the specified username
|
||||
* within the player collection.
|
||||
|
||||
@@ -66,7 +66,7 @@ public enum EventType {
|
||||
*/
|
||||
ENDED_GAME("Ended Game");
|
||||
|
||||
private String description;
|
||||
private final String description;
|
||||
private EventType(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ public interface IClient {
|
||||
* and preferred connection type.
|
||||
*
|
||||
* @param username the username chosen by the player.
|
||||
* @param preferredInt the preferred connection type selected by the client.
|
||||
* @param proposedNPlayers the desired number of players proposed by this client.
|
||||
* @return {@code true} if the connection is established successfully,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
ErrorType connect(String username, int preferredInt);
|
||||
ErrorType connect(String username, int proposedNPlayers);
|
||||
|
||||
/**
|
||||
* Requests to draw a tribe card from the upper tribe card list.
|
||||
|
||||
@@ -12,6 +12,10 @@ public final class NetworkConfig {
|
||||
public static final long SILENCE_THRESHOLD_MS = 5_000;
|
||||
/** Milliseconds between keep-alive pings sent by the client. */
|
||||
public static final long KEEPALIVE_INTERVAL_MS = 3_000;
|
||||
/** Minimum number of players required to start a game. */
|
||||
public static final int MIN_PLAYERS = 2;
|
||||
/** Maximum number of players allowed in a game. */
|
||||
public static final int MAX_PLAYERS = 5;
|
||||
|
||||
private NetworkConfig() {}
|
||||
}
|
||||
|
||||
@@ -80,15 +80,6 @@ public abstract class NetworkEvent implements Serializable {
|
||||
*/
|
||||
protected ArrayList<String> disconnectedPlayers;
|
||||
|
||||
/**
|
||||
* Sets the list of currently disconnected player usernames.
|
||||
*
|
||||
* @param players the list of disconnected player usernames to attach to this event.
|
||||
*/
|
||||
public void setDisconnected(ArrayList<String> players) {
|
||||
this.disconnectedPlayers = players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates this event with the current game state so clients can
|
||||
* update their local model after receiving it.
|
||||
@@ -140,7 +131,7 @@ public abstract class NetworkEvent implements Serializable {
|
||||
* @return {@code true} if the event could not be applied successfully,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean getIsError() {
|
||||
public boolean isError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
@@ -154,6 +145,15 @@ public abstract class NetworkEvent implements Serializable {
|
||||
this.isError = isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the error type for this event.
|
||||
*
|
||||
* @param errorType the error type to set.
|
||||
*/
|
||||
public void setErrorType(ErrorType errorType) {
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a network event.
|
||||
*
|
||||
@@ -162,7 +162,7 @@ public abstract class NetworkEvent implements Serializable {
|
||||
* @param isError {@code true} if the event represents an error,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
protected NetworkEvent(String username, EventType eventType, boolean isError,ErrorType errorType) {
|
||||
protected NetworkEvent(String username, EventType eventType, boolean isError, ErrorType errorType) {
|
||||
this.username = username;
|
||||
this.eventType = eventType;
|
||||
this.isError = isError;
|
||||
|
||||
@@ -7,20 +7,15 @@ import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* NetworkEvent to add a player.
|
||||
* Network event used to register a new player in the lobby.
|
||||
*
|
||||
* <p>The first player to join also proposes the desired number of players
|
||||
* for the match. Subsequent joiners ignore {@code proposedNPlayer} if the
|
||||
* game has already been created.
|
||||
*/
|
||||
public class AddPlayer extends NetworkEvent {
|
||||
/** Number of proposed players to add to the match */
|
||||
/** Number of players proposed by this player; used only when creating a new game. */
|
||||
private int proposedNPlayer;
|
||||
/**
|
||||
* Overrides the default error type for this event.
|
||||
*
|
||||
* @param errorType the error type to set.
|
||||
*/
|
||||
public void setErrorType(ErrorType errorType)
|
||||
{
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of proposed players to add to the match
|
||||
|
||||
@@ -17,45 +17,60 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* NetworkEvent to draw a tribe card from the lower card list.
|
||||
* Network event that transitions all clients to the next round.
|
||||
*
|
||||
* <p>Carries refreshed card lists for all four board rows in addition
|
||||
* to the standard game state (slot map, turn order, current state,
|
||||
* player list). Only applied client-side; {@link #apply(GameController)}
|
||||
* always returns {@code false}.
|
||||
*/
|
||||
public class ApplyNextRound extends NetworkEvent{
|
||||
public class ApplyNextRound extends NetworkEvent {
|
||||
|
||||
List<Player> players;
|
||||
ArrayList<TribeCard> upperListTribeCards;
|
||||
ArrayList<TribeCard> lowerListTribeCards;
|
||||
ArrayList<BuildingCard> upperListBuildingCards;
|
||||
ArrayList<BuildingCard> lowerListBuildingCards;
|
||||
private ArrayList<TribeCard> upperListTribeCards;
|
||||
private ArrayList<TribeCard> lowerListTribeCards;
|
||||
private ArrayList<BuildingCard> upperListBuildingCards;
|
||||
private ArrayList<BuildingCard> lowerListBuildingCards;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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.
|
||||
* @param upperListTribeCards the upper row of tribe cards for the new round.
|
||||
* @param lowerListTribeCards the lower row of tribe cards for the new round.
|
||||
* @param upperListBuildingCards the upper row of building cards for the new round.
|
||||
* @param lowerListBuildingCards the lower row of building cards for the new round.
|
||||
*/
|
||||
public ApplyNextRound(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, List<Player> players, ArrayList<TribeCard> upperListTribeCards, ArrayList<TribeCard>lowerListTribeCards, ArrayList<BuildingCard> upperListBuildingCards, ArrayList<BuildingCard>lowerListBuildingCards) {
|
||||
super("SERVER",EventType.NEXT_ROUND,false, ErrorType.WRONG_ACTION);
|
||||
public ApplyNextRound(Map<Slot, Player> slotPlayerMap,
|
||||
OrderLogicCard orderLogicCard,
|
||||
CurrentState currentState,
|
||||
List<Player> players,
|
||||
ArrayList<TribeCard> upperListTribeCards,
|
||||
ArrayList<TribeCard> lowerListTribeCards,
|
||||
ArrayList<BuildingCard> upperListBuildingCards,
|
||||
ArrayList<BuildingCard> lowerListBuildingCards) {
|
||||
super("SERVER", EventType.NEXT_ROUND, false, ErrorType.WRONG_ACTION);
|
||||
setData(slotPlayerMap, orderLogicCard, currentState, players);
|
||||
this.upperListBuildingCards = upperListBuildingCards;
|
||||
this.lowerListBuildingCards = lowerListBuildingCards;
|
||||
this.upperListTribeCards = upperListTribeCards;
|
||||
this.lowerListTribeCards = lowerListTribeCards;
|
||||
this.slotPlayerMap = slotPlayerMap;
|
||||
this.orderLogicCard = orderLogicCard;
|
||||
this.currentState = currentState;
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gameController the Game Controller on which to apply the event
|
||||
* @return true if the player could draw the card, false otherwise
|
||||
* Not applicable server-side.
|
||||
*
|
||||
* @param gameController the game controller (unused).
|
||||
* @return {@code false} always.
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(GameController gameController){
|
||||
public boolean apply(GameController gameController) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the client-side mini model, updating all board lists,
|
||||
* slot map, turn order, and game state for the new round.
|
||||
@@ -63,20 +78,19 @@ public class ApplyNextRound extends NetworkEvent{
|
||||
* @param miniModel the client-side model to update.
|
||||
* @return {@code true} always (this event cannot produce an error).
|
||||
*/
|
||||
public boolean apply(MiniModel miniModel)
|
||||
{
|
||||
@Override
|
||||
public boolean apply(MiniModel miniModel) {
|
||||
synchronized (miniModel) {
|
||||
miniModel.setSlotPlayerMap(slotPlayerMap);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
miniModel.setPlayers(players);
|
||||
miniModel.upperListTribeCards = upperListTribeCards;
|
||||
miniModel.lowerListTribeCards = lowerListTribeCards;
|
||||
miniModel.upperListBuildingCards = upperListBuildingCards;
|
||||
miniModel.lowerListBuildingCards = lowerListBuildingCards;
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setUpperListTribeCards(upperListTribeCards);
|
||||
miniModel.setLowerListTribeCards(lowerListTribeCards);
|
||||
miniModel.setUpperListBuildingCards(upperListBuildingCards);
|
||||
miniModel.setLowerListBuildingCards(lowerListBuildingCards);
|
||||
miniModel.setLastEvent(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,14 +9,16 @@ import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Model.Game;
|
||||
import it.polimi.ingsw.gc14.Model.Totems;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* NetworkEvent to avoid drawing a card from the lower card list
|
||||
* Network event used to notify all clients that a player has disconnected.
|
||||
*
|
||||
* <p>Also carries the updated list of available totems, since a disconnection
|
||||
* during totem selection may free a totem for other players.
|
||||
*/
|
||||
public class DisconnectedPlayer extends NetworkEvent implements Serializable{
|
||||
public class DisconnectedPlayer extends NetworkEvent {
|
||||
|
||||
private List<Totems> availableTotems;
|
||||
|
||||
@@ -29,18 +31,19 @@ public class DisconnectedPlayer extends NetworkEvent implements Serializable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* Constructs a disconnection event for the specified player.
|
||||
*
|
||||
* @param username the username of the player who disconnected.
|
||||
*/
|
||||
public DisconnectedPlayer(String username){
|
||||
public DisconnectedPlayer(String username) {
|
||||
super(username, EventType.DISCONNECTED_PLAYER, false, ErrorType.GENERIC_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param gameController the Game Controller on which to apply the event
|
||||
* @return true if the player could skipTheTurn, false otherwise
|
||||
* Applies this disconnection event to the server-side game controller.
|
||||
*
|
||||
* @param gameController the game controller on which to apply the event.
|
||||
* @return {@code true} if the disconnection is handled successfully.
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(GameController gameController){
|
||||
|
||||
@@ -16,7 +16,7 @@ public class DrawLowerBuildingCard extends NetworkEvent{
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initialized all the attributes.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* @param pos the index of the card to draw
|
||||
*/
|
||||
@@ -48,7 +48,7 @@ public class DrawLowerBuildingCard extends NetworkEvent{
|
||||
if (isError)
|
||||
return false;
|
||||
|
||||
miniModel.lowerListBuildingCards.remove(pos);
|
||||
miniModel.removeLowerBuildingCard(pos);
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DrawLowerTribeCard extends NetworkEvent{
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initialized all the attributes.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* @param pos the index of the card to draw
|
||||
*/
|
||||
@@ -47,7 +47,7 @@ public class DrawLowerTribeCard extends NetworkEvent{
|
||||
if (isError)
|
||||
return false;
|
||||
|
||||
miniModel.lowerListTribeCards.remove(pos);
|
||||
miniModel.removeLowerTribeCard(pos);
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DrawUpperBuildingCard extends NetworkEvent{
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initialized all the attributes.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* @param pos the index of the card to draw
|
||||
*/
|
||||
@@ -48,7 +48,7 @@ public class DrawUpperBuildingCard extends NetworkEvent{
|
||||
if (isError)
|
||||
return false;
|
||||
|
||||
miniModel.upperListBuildingCards.remove(pos);
|
||||
miniModel.removeUpperBuildingCard(pos);
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DrawUpperTribeCard extends NetworkEvent{
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initialized all the attributes.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* @param pos the index of the card to draw
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ public class DrawUpperTribeCard extends NetworkEvent{
|
||||
synchronized (miniModel) {
|
||||
if (isError)
|
||||
return false;
|
||||
miniModel.upperListTribeCards.remove(pos);
|
||||
miniModel.removeUpperTribeCard(pos);
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
|
||||
@@ -11,58 +11,59 @@ import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* NetworkEvent to draw a tribe card from the lower card list.
|
||||
* Network event that signals the end of the game and delivers the final standings.
|
||||
*
|
||||
* <p>Only applied client-side; {@link #apply(GameController)} always returns {@code false}.
|
||||
*/
|
||||
public class EndedGame extends NetworkEvent{
|
||||
|
||||
ArrayList<Player> players;
|
||||
public class EndedGame extends NetworkEvent {
|
||||
|
||||
/**
|
||||
* Constructs an event containing the final game state.
|
||||
*
|
||||
* @param slotPlayerMap the map associating each slot with the player occupying it.
|
||||
* @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.
|
||||
* @param currentState the current state of the game.
|
||||
* @param players the final list of players, used as the standings.
|
||||
*/
|
||||
public EndedGame(Map<Slot, Player> slotPlayerMap, OrderLogicCard orderLogicCard, CurrentState currentState, ArrayList<Player> players){
|
||||
super("SERVER",EventType.ENDED_GAME,false, ErrorType.GENERIC_ERROR);
|
||||
this.slotPlayerMap = slotPlayerMap;
|
||||
this.orderLogicCard = orderLogicCard;
|
||||
this.currentState = currentState;
|
||||
this.players = players;
|
||||
public EndedGame(Map<Slot, Player> slotPlayerMap,
|
||||
OrderLogicCard orderLogicCard,
|
||||
CurrentState currentState,
|
||||
ArrayList<Player> players) {
|
||||
super("SERVER", EventType.ENDED_GAME, false, ErrorType.GENERIC_ERROR);
|
||||
setData(slotPlayerMap, orderLogicCard, currentState, players);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gameController the Game Controller on which to apply the event
|
||||
* @return true if the player could draw the card, false otherwise
|
||||
* Not applicable server-side.
|
||||
*
|
||||
* @param gameController the game controller (unused).
|
||||
* @return {@code false} always.
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(GameController gameController){
|
||||
public boolean apply(GameController gameController) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this event to the client-side mini model, updating the final
|
||||
* game state and setting the standing players list for the leaderboard.
|
||||
* game state and setting the standings list for the leaderboard.
|
||||
*
|
||||
* @param miniModel the client-side model to update.
|
||||
* @return {@code true} always (this event cannot produce an error).
|
||||
*/
|
||||
public boolean apply(MiniModel miniModel)
|
||||
{
|
||||
@Override
|
||||
public boolean apply(MiniModel miniModel) {
|
||||
synchronized (miniModel) {
|
||||
miniModel.setSlotPlayerMap(slotPlayerMap);
|
||||
miniModel.setOrderLogicCard(orderLogicCard);
|
||||
miniModel.setCurrentState(currentState);
|
||||
miniModel.setPlayers(players);
|
||||
miniModel.setStandingPlayers(players);
|
||||
miniModel.setPlayers(playerList);
|
||||
miniModel.setStandingPlayers(playerList);
|
||||
miniModel.setLastEvent(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@ import it.polimi.ingsw.gc14.Model.MiniModel;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
@Override
|
||||
public void enrichWithGameState(Game game, ArrayList<String> disconnectedUsernames) {
|
||||
|
||||
@@ -6,12 +6,10 @@ import it.polimi.ingsw.gc14.Model.MiniModel;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* NetworkEvent to avoid drawing a card from the lower card list
|
||||
* Network event used to skip the current player's optional card action.
|
||||
*/
|
||||
public class SkipTurn extends NetworkEvent implements Serializable{
|
||||
public class SkipTurn extends NetworkEvent {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
|
||||
@@ -12,14 +12,14 @@ import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
*/
|
||||
public class SlotChoice extends NetworkEvent {
|
||||
|
||||
/** Index of the card to draw */
|
||||
/** Index of the selected slot. */
|
||||
private int pos;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
* Initializes all the attributes.
|
||||
* @param username the name of the player requesting the event
|
||||
* @param pos the index of the card to draw
|
||||
* @param pos the index of the selected slot
|
||||
*/
|
||||
public SlotChoice(String username, int pos) {
|
||||
super(username, EventType.SLOT_CHOICE, false, ErrorType.WRONG_ACTION);
|
||||
@@ -27,8 +27,8 @@ public class SlotChoice extends NetworkEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gameController the Game Controller on which to apply the event
|
||||
* @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 slot choice is handled successfully, {@code false} otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(GameController gameController) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.RMI.Common.IClientCallback;
|
||||
import javafx.application.Platform;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
|
||||
@@ -15,7 +14,7 @@ import java.rmi.server.UnicastRemoteObject;
|
||||
* RMI client callback implementation of {@link IClientCallback}.
|
||||
* Receives notifications from the server and updates the client game model.
|
||||
*/
|
||||
public class ClientCallbackImpl extends UnicastRemoteObject implements IClientCallback, Serializable {
|
||||
public class ClientCallbackImpl extends UnicastRemoteObject implements IClientCallback {
|
||||
|
||||
/** The client controller used to apply events and update the model */
|
||||
private final ClientController clientController;
|
||||
@@ -53,7 +52,7 @@ public class ClientCallbackImpl extends UnicastRemoteObject implements IClientCa
|
||||
*/
|
||||
@Override
|
||||
public void onAction(NetworkEvent event) throws RemoteException {
|
||||
if(event.getIsError()) {
|
||||
if(event.isError()) {
|
||||
clientController.getView().showError(event.getErrorType(),event.toString());
|
||||
} else {
|
||||
event.apply(clientController.getMiniModel());
|
||||
|
||||
@@ -20,7 +20,7 @@ public class RMIClient implements IClient {
|
||||
private final int port;
|
||||
private IGameServer stub;
|
||||
private ClientController controller;
|
||||
private String myIP;
|
||||
private String myIp;
|
||||
private String username;
|
||||
|
||||
private volatile boolean running = false;
|
||||
@@ -35,13 +35,13 @@ public class RMIClient implements IClient {
|
||||
* @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.
|
||||
* @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.host = host;
|
||||
this.port = port;
|
||||
this.myIP = myIP;
|
||||
this.myIp = myIp;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,15 +53,15 @@ public class RMIClient implements IClient {
|
||||
* opening a second socket).
|
||||
*/
|
||||
@Override
|
||||
public ErrorType connect(String username, int preferredInt) {
|
||||
public ErrorType connect(String username, int proposedNPlayers) {
|
||||
try {
|
||||
System.setProperty("java.rmi.server.hostname", this.myIP);
|
||||
System.setProperty("java.rmi.server.hostname", this.myIp);
|
||||
Registry registry = LocateRegistry.getRegistry(host, port);
|
||||
this.stub = (IGameServer) registry.lookup("RMIGameServer");
|
||||
this.username = username;
|
||||
|
||||
ClientCallbackImpl callback = new ClientCallbackImpl(controller);
|
||||
ErrorType status = stub.joinGame(username, preferredInt, callback);
|
||||
ErrorType status = stub.joinGame(username, proposedNPlayers, callback);
|
||||
if(status==null)
|
||||
{
|
||||
running = true;
|
||||
@@ -250,12 +250,12 @@ public class RMIClient implements IClient {
|
||||
* the keep-alive scheduler.
|
||||
*/
|
||||
public void notifyDisconnection() {
|
||||
running = false;
|
||||
if (pingSender != null) pingSender.shutdownNow();
|
||||
if (executor != null) executor.shutdownNow();
|
||||
try{
|
||||
try {
|
||||
stub.disconnectPlayer(username);
|
||||
}
|
||||
catch (RemoteException e){
|
||||
} catch (RemoteException e) {
|
||||
System.out.println("Error during remote disconnection");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ public interface IGameServer extends Remote {
|
||||
* 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 preferredInt the desired number of players proposed by this client.
|
||||
* @param callback the remote callback associated with the client.
|
||||
* @return {@code true} if the player joins the game successfully,
|
||||
* {@code false} otherwise.
|
||||
* @return {@code null} if the player joins successfully;
|
||||
* an {@link it.polimi.ingsw.gc14.ErrorType} value describing the rejection otherwise.
|
||||
* @throws RemoteException if an RMI communication error occurs.
|
||||
*/
|
||||
ErrorType joinGame(String username, int preferredInt, IClientCallback callback) throws RemoteException;
|
||||
@@ -96,5 +96,11 @@ public interface IGameServer extends Remote {
|
||||
*/
|
||||
void ping(String username) throws RemoteException;
|
||||
|
||||
/**
|
||||
* Notifies the server of a voluntary disconnection for the specified player.
|
||||
*
|
||||
* @param username the username of the player disconnecting.
|
||||
* @throws RemoteException if an RMI communication error occurs.
|
||||
*/
|
||||
void disconnectPlayer(String username) throws RemoteException;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import it.polimi.ingsw.gc14.LimitedMap;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkConfig;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.DisconnectedPlayer;
|
||||
import it.polimi.ingsw.gc14.Network.RMI.Common.IClientCallback;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
@@ -28,7 +29,7 @@ public class RMIHeartbeat {
|
||||
|
||||
private String username = "";
|
||||
private final LimitedMap<String, Boolean> playerList;
|
||||
private final Map<String, ?> clients; // ConcurrentHashMap<String, IClientCallback>
|
||||
private final Map<String, IClientCallback> clients;
|
||||
private final BlockingQueue<NetworkEvent> actionQueue;
|
||||
|
||||
/** Last time a ping was received from this client. */
|
||||
@@ -53,7 +54,7 @@ public class RMIHeartbeat {
|
||||
public RMIHeartbeat(
|
||||
String username,
|
||||
LimitedMap<String, Boolean> playerList,
|
||||
Map<String, ?> clients,
|
||||
Map<String, IClientCallback> clients,
|
||||
BlockingQueue<NetworkEvent> actionQueue) {
|
||||
|
||||
this.username = username;
|
||||
|
||||
@@ -6,6 +6,7 @@ import it.polimi.ingsw.gc14.LimitedMap;
|
||||
import it.polimi.ingsw.gc14.Model.Game;
|
||||
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
|
||||
import it.polimi.ingsw.gc14.Model.MiniModel;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkConfig;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.*;
|
||||
import it.polimi.ingsw.gc14.Network.RMI.Common.IClientCallback;
|
||||
@@ -33,10 +34,10 @@ import java.util.stream.Collectors;
|
||||
* shared network event queue.
|
||||
*/
|
||||
public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
private String host;
|
||||
private final String host;
|
||||
private final GameController controller;
|
||||
private Registry registry;
|
||||
private int nPort;
|
||||
private final int nPort;
|
||||
|
||||
/** username → callback */
|
||||
private final Map<String, IClientCallback> clients = new ConcurrentHashMap<>();
|
||||
@@ -48,8 +49,8 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
*/
|
||||
private final Map<String, RMIHeartbeat> watchdogs = new ConcurrentHashMap<>();
|
||||
|
||||
BlockingQueue<NetworkEvent> actionQueue;
|
||||
private LimitedMap<String, Boolean> playerList;
|
||||
private final BlockingQueue<NetworkEvent> actionQueue;
|
||||
private final LimitedMap<String, Boolean> playerList;
|
||||
|
||||
|
||||
/**
|
||||
@@ -80,14 +81,17 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>After a successful join a {@link RMIHeartbeat} is created and
|
||||
* <p>After a successful join an {@link RMIHeartbeat} watchdog is created and
|
||||
* started for the new player — mirrors creating a {@code HeartbeatHandler} in
|
||||
* {@code TCPServer.acceptHeartbeat()}.
|
||||
*
|
||||
* @return {@code null} on success; an {@link it.polimi.ingsw.gc14.ErrorType} value on rejection.
|
||||
*/
|
||||
@Override
|
||||
public ErrorType joinGame(String username, int preferredInt, IClientCallback callback)
|
||||
throws RemoteException {
|
||||
if (preferredInt < 2 || preferredInt > 5) return ErrorType.WRONG_PLAYER_NUMBER;
|
||||
if (preferredInt < NetworkConfig.MIN_PLAYERS || preferredInt > NetworkConfig.MAX_PLAYERS)
|
||||
return ErrorType.WRONG_PLAYER_NUMBER;
|
||||
|
||||
synchronized (controller) {
|
||||
|
||||
@@ -97,7 +101,7 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
System.out.println("Game Created With :"+preferredInt+" Players");
|
||||
}
|
||||
if(controller.getModel().getCurrentState().getGameStage()!= GameStages.WAITING ) {
|
||||
if (!controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))) {
|
||||
if (controller.getModel().getPlayers().stream().noneMatch(p -> p.getUserName().equals(username))) {
|
||||
return ErrorType.GAME_ALREADY_STARTED;
|
||||
}
|
||||
}
|
||||
@@ -129,7 +133,12 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
System.out.println("Reconnected player: " + username);
|
||||
startWatchdog(username);
|
||||
Game game = controller.getModel();
|
||||
callback.onGameInit(new MiniModel(game.getSlotMap(), game.getOrderLogicCard(), game.getCurrentState(), game.getPlayers(), game.getAvailableTotems(), game.getUpperListTribeCards(), game.getLowerListTribeCards(), game.getUpperListBuilding(), game.getLowerListBuilding(), game.getDisconnectedPlayers().entrySet().stream().filter(Map.Entry::getValue).map(x -> x.getKey().getUserName()).collect(Collectors.toCollection(ArrayList::new))));
|
||||
callback.onGameInit(new MiniModel(
|
||||
game.getSlotMap(), game.getOrderLogicCard(), game.getCurrentState(),
|
||||
game.getPlayers(), game.getAvailableTotems(),
|
||||
game.getUpperListTribeCards(), game.getLowerListTribeCards(),
|
||||
game.getUpperListBuilding(), game.getLowerListBuilding(),
|
||||
disconnectedUsernames(game)));
|
||||
System.out.println("Model sent: " + username);
|
||||
actionQueue.add(new ReconnectPlayer(username));
|
||||
return null;
|
||||
@@ -172,8 +181,7 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
*/
|
||||
public void notifyAll(NetworkEvent action){
|
||||
for (Map.Entry<String, IClientCallback> entry : clients.entrySet()) {
|
||||
if (!action.getIsError() ||
|
||||
(action.getIsError() && action.getUsername().equals(entry.getKey()))) {
|
||||
if (!action.isError() || action.getUsername().equals(entry.getKey())) {
|
||||
try{
|
||||
entry.getValue().onAction(action);
|
||||
}
|
||||
@@ -300,7 +308,7 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
registry.rebind("RMIGameServer", this);
|
||||
System.out.println("RMI Server started on port: " + nPort);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
@@ -319,7 +327,8 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
try {
|
||||
registry.unbind("RMIGameServer");
|
||||
UnicastRemoteObject.unexportObject(this, true);
|
||||
watchdogs.values().forEach(wd -> { /* watchdogs shut themselves down */ });
|
||||
watchdogs.values().forEach(RMIHeartbeat::disconnect);
|
||||
watchdogs.clear();
|
||||
System.out.println("RMI Server stopped");
|
||||
return true;
|
||||
} catch (RemoteException | NotBoundException e) {
|
||||
@@ -346,11 +355,20 @@ public class RMIServer extends UnicastRemoteObject implements IGameServer {
|
||||
*
|
||||
* @param username the username of the player to disconnect.
|
||||
*/
|
||||
@Override
|
||||
public void disconnectPlayer(String username) {
|
||||
RMIHeartbeat wd = watchdogs.get(username);
|
||||
if (wd != null) wd.disconnect();
|
||||
}
|
||||
|
||||
/** Returns usernames of players currently marked as disconnected in the given game. */
|
||||
private static ArrayList<String> disconnectedUsernames(Game game) {
|
||||
return game.getDisconnectedPlayers().entrySet().stream()
|
||||
.filter(Map.Entry::getValue)
|
||||
.map(e -> e.getKey().getUserName())
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,25 +22,25 @@ public class TCPClient implements IClient {
|
||||
private static final int PONG = 2;
|
||||
|
||||
/** Socket TCP */
|
||||
Socket communicationSocket;
|
||||
private Socket communicationSocket;
|
||||
|
||||
/** Input stream used receive objects from the server */
|
||||
ObjectInputStream socketReceive;
|
||||
private ObjectInputStream socketReceive;
|
||||
|
||||
/** Output stream used to send objects to the server */
|
||||
ObjectOutputStream socketSend;
|
||||
private ObjectOutputStream socketSend;
|
||||
|
||||
/** Client game's controller */
|
||||
ClientController controller;
|
||||
private ClientController controller;
|
||||
|
||||
/** IP address of the server to connect to */
|
||||
String hostname;
|
||||
private String hostname;
|
||||
|
||||
private boolean running;
|
||||
/** TCP port */
|
||||
int mainPort;
|
||||
private int mainPort;
|
||||
|
||||
int heartbeatPort;
|
||||
private int heartbeatPort;
|
||||
private Socket heartbeatSocket;
|
||||
private OutputStream heartbeatOut;
|
||||
private InputStream heartbeatIn;
|
||||
@@ -80,26 +80,27 @@ public class TCPClient implements IClient {
|
||||
try {
|
||||
if( socketReceive.readObject() instanceof AddPlayer x)
|
||||
{
|
||||
if(x.getIsError())
|
||||
if(x.isError())
|
||||
return x.getErrorType();
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
return ErrorType.GENERIC_ERROR;
|
||||
}
|
||||
|
||||
running = true;
|
||||
|
||||
new Thread(this::receiveMessage, "tcp-reader").start();
|
||||
|
||||
// Socket heartbeat
|
||||
// heartbeat socket
|
||||
this.heartbeatSocket = new Socket(hostname, heartbeatPort);
|
||||
this.heartbeatOut =heartbeatSocket.getOutputStream() ;
|
||||
this.heartbeatOut = heartbeatSocket.getOutputStream();
|
||||
this.heartbeatIn = heartbeatSocket.getInputStream();
|
||||
|
||||
// manda subito username per associare i due socket lato server
|
||||
// send username immediately so server can associate the two sockets
|
||||
new ObjectOutputStream(heartbeatSocket.getOutputStream()).writeObject(user);
|
||||
heartbeatOut.flush();
|
||||
new Thread(this::heartbeatLoop, "heartbeat").start();
|
||||
|
||||
running = true;
|
||||
return null;
|
||||
|
||||
} catch (IOException e) {
|
||||
@@ -135,23 +136,20 @@ public class TCPClient implements IClient {
|
||||
int b = heartbeatIn.read();
|
||||
if (b == -1 || b != PONG) {
|
||||
disconnect();
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED, ErrorType.SERVER_CRASHED.toString());
|
||||
break;
|
||||
}
|
||||
// pong ricevuto → server vivo
|
||||
// pong received: server is alive
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println("Server heartbeat timeout");
|
||||
disconnect();
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
|
||||
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED, ErrorType.SERVER_CRASHED.toString());
|
||||
} catch (IOException e) {
|
||||
if(running)
|
||||
{
|
||||
if (running) {
|
||||
disconnect();
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED,ErrorType.SERVER_CRASHED.toString());
|
||||
controller.getView().showError(ErrorType.SERVER_CRASHED, ErrorType.SERVER_CRASHED.toString());
|
||||
}
|
||||
|
||||
} finally {
|
||||
sender.shutdownNow();
|
||||
}
|
||||
@@ -170,7 +168,7 @@ public class TCPClient implements IClient {
|
||||
* Listens continuously for incoming objects from the server.
|
||||
* - If the received object is a {@link NetworkEvent} flagged as an error, it is printed.
|
||||
* - If the received object is a valid {@link NetworkEvent}, it is applied to the game controller.
|
||||
* - If the received object is a {@link Game} model, the controller's model is set.
|
||||
* - If the received object is a {@link MiniModel}, the controller's model is set.
|
||||
*/
|
||||
private void receiveMessage() {
|
||||
while (true) {
|
||||
@@ -179,11 +177,12 @@ public class TCPClient implements IClient {
|
||||
try {
|
||||
read = socketReceive.readObject();
|
||||
} catch (IOException e) {
|
||||
if (running) disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
if (read instanceof NetworkEvent event) {
|
||||
if (event.getIsError()) {
|
||||
if (event.isError()) {
|
||||
if(event.getErrorType() == ErrorType.WRONG_ACTION) {
|
||||
controller.getView().showError(ErrorType.WRONG_ACTION,event.toString());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class ClientHandler implements Runnable {
|
||||
*/
|
||||
private final String username;
|
||||
|
||||
private boolean running ;
|
||||
private volatile boolean running;
|
||||
|
||||
/**
|
||||
* Returns the username associated with this client.
|
||||
@@ -37,22 +37,22 @@ public class ClientHandler implements Runnable {
|
||||
private Socket clientSocket;
|
||||
|
||||
/** Input stream used to receive objects from the client */
|
||||
public ObjectInputStream in;
|
||||
private ObjectInputStream in;
|
||||
|
||||
/** Output stream used to send objects to the client */
|
||||
public ObjectOutputStream out;
|
||||
private ObjectOutputStream out;
|
||||
|
||||
/**
|
||||
* Shared list of all client handlers.
|
||||
* This handler removes itself from the list when disconnected.
|
||||
*/
|
||||
List<ClientHandler> clientHandlers;
|
||||
private List<ClientHandler> clientHandlers;
|
||||
|
||||
/** Maps each connected player's username to their connection state (true = connected). */
|
||||
LimitedMap<String,Boolean> limitedMap;
|
||||
private LimitedMap<String, Boolean> limitedMap;
|
||||
|
||||
/** Queue containing the events to be applied to the game model */
|
||||
BlockingQueue<NetworkEvent> actionQueue;
|
||||
private BlockingQueue<NetworkEvent> actionQueue;
|
||||
|
||||
|
||||
/**
|
||||
@@ -87,7 +87,7 @@ public class ClientHandler implements Runnable {
|
||||
try {
|
||||
while (running) {
|
||||
NetworkEvent event = (NetworkEvent) in.readObject();
|
||||
if (!actionQueue.add(event)) {
|
||||
if (!actionQueue.offer(event)) {
|
||||
System.out.println("Error inserting action into queue");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +84,11 @@ public class HeartbeatHandler implements Runnable {
|
||||
startWatchdog();
|
||||
try {
|
||||
while (running) {
|
||||
int b = in.read(); // blocca finché non arriva un byte
|
||||
int b = in.read(); // blocks until a byte arrives
|
||||
if (b == -1) {
|
||||
synchronized (this)
|
||||
{
|
||||
out.write(-1);
|
||||
}
|
||||
disconnect(); break;
|
||||
} // stream chiusa
|
||||
disconnect();
|
||||
break; // stream closed
|
||||
}
|
||||
if (b == PING) {
|
||||
lastReceivedTime = System.currentTimeMillis();
|
||||
out.write(PONG);
|
||||
|
||||
@@ -7,6 +7,7 @@ import it.polimi.ingsw.gc14.Model.Game;
|
||||
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
|
||||
import it.polimi.ingsw.gc14.Model.MiniModel;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkConfig;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvent;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.AddPlayer;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkEvents.ReconnectPlayer;
|
||||
@@ -34,47 +35,42 @@ public class TCPServer {
|
||||
/**
|
||||
* Main TCP port used for standard client-server communication.
|
||||
*/
|
||||
int port;
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* TCP port dedicated to heartbeat communication.
|
||||
*/
|
||||
int heartbeatPort;
|
||||
|
||||
/**
|
||||
* Number of players that have successfully connected.
|
||||
*/
|
||||
int connectedPlayers;
|
||||
private int heartbeatPort;
|
||||
|
||||
/**
|
||||
* Main server socket used to accept client connections.
|
||||
*/
|
||||
ServerSocket socketTCP;
|
||||
private ServerSocket socketTCP;
|
||||
|
||||
/**
|
||||
* Server socket used to accept heartbeat connections.
|
||||
*/
|
||||
ServerSocket heartbeatSocketTCP;
|
||||
private ServerSocket heartbeatSocketTCP;
|
||||
|
||||
/**
|
||||
* Game controller used to manage the server-side game logic.
|
||||
*/
|
||||
final GameController controller;
|
||||
private final GameController controller;
|
||||
|
||||
/**
|
||||
* Queue containing network events received from clients.
|
||||
*/
|
||||
BlockingQueue<NetworkEvent> actionQueue;
|
||||
private BlockingQueue<NetworkEvent> actionQueue;
|
||||
|
||||
/**
|
||||
* Map storing the online/offline status of connected players.
|
||||
*/
|
||||
LimitedMap<String, Boolean> playerList;
|
||||
private LimitedMap<String, Boolean> playerList;
|
||||
|
||||
/**
|
||||
* List of active TCP client handlers.
|
||||
*/
|
||||
CopyOnWriteArrayList<ClientHandler> clientHandlers;
|
||||
private CopyOnWriteArrayList<ClientHandler> clientHandlers;
|
||||
|
||||
|
||||
/**
|
||||
@@ -99,7 +95,6 @@ public class TCPServer {
|
||||
LimitedMap<String, Boolean> playerList) {
|
||||
this.port = port;
|
||||
this.heartbeatPort = heartbeatPort;
|
||||
this.connectedPlayers = 0;
|
||||
this.controller = controller;
|
||||
this.actionQueue = actionQueue;
|
||||
this.playerList = playerList;
|
||||
@@ -152,8 +147,8 @@ public class TCPServer {
|
||||
|
||||
AddPlayer eventAddPlayer = (AddPlayer) event;
|
||||
|
||||
if (eventAddPlayer.getProposedNPlayer() < 2
|
||||
|| eventAddPlayer.getProposedNPlayer() > 5) {
|
||||
if (eventAddPlayer.getProposedNPlayer() < NetworkConfig.MIN_PLAYERS
|
||||
|| eventAddPlayer.getProposedNPlayer() > NetworkConfig.MAX_PLAYERS) {
|
||||
eventAddPlayer.setErrorType(ErrorType.WRONG_PLAYER_NUMBER);
|
||||
eventAddPlayer.setIsError(true);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
@@ -172,7 +167,7 @@ public class TCPServer {
|
||||
System.out.println("Game Created With :"+eventAddPlayer.getProposedNPlayer()+" Players");
|
||||
}
|
||||
if(controller.getModel().getCurrentState().getGameStage()!= GameStages.WAITING ) {
|
||||
if (!controller.getModel().getPlayers().stream().anyMatch(p -> p.getUserName().equals(username))) {
|
||||
if (controller.getModel().getPlayers().stream().noneMatch(p -> p.getUserName().equals(username))) {
|
||||
eventAddPlayer.setErrorType(ErrorType.GAME_ALREADY_STARTED);
|
||||
eventAddPlayer.setIsError(true);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
@@ -181,33 +176,16 @@ public class TCPServer {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else{
|
||||
else {
|
||||
if (controller.addPlayer(username)) {
|
||||
playerList.put(username, true);
|
||||
System.out.println("Accepted player: " + username);
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username,
|
||||
clientSocket,
|
||||
clientSend,
|
||||
clientReceive,
|
||||
clientHandlers,
|
||||
playerList,
|
||||
actionQueue
|
||||
);
|
||||
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
|
||||
clientHandlers.add(handler);
|
||||
connectedPlayers++;
|
||||
ClientHandler handler = createAndRegisterHandler(
|
||||
username, clientSocket, clientSend, clientReceive);
|
||||
new Thread(handler).start();
|
||||
continue;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
eventAddPlayer.setErrorType(ErrorType.USERNAME_ALREADY_USED);
|
||||
eventAddPlayer.setIsError(true);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
@@ -217,73 +195,36 @@ public class TCPServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!playerList.containsKey(username)) {
|
||||
playerList.put(username, true);
|
||||
System.out.println("(After crash)Reconnected player: " + username);
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username,
|
||||
clientSocket,
|
||||
clientSend,
|
||||
clientReceive,
|
||||
clientHandlers,
|
||||
playerList,
|
||||
actionQueue
|
||||
);
|
||||
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
|
||||
clientHandlers.add(handler);
|
||||
connectedPlayers++;
|
||||
}
|
||||
//reconnect a previously disconnected player
|
||||
else{
|
||||
if (!playerList.get(username)) {
|
||||
|
||||
playerList.put(username, true);
|
||||
System.out.println("Reconnected player: " + username);
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username,
|
||||
clientSocket,
|
||||
clientSend,
|
||||
clientReceive,
|
||||
clientHandlers,
|
||||
playerList,
|
||||
actionQueue
|
||||
);
|
||||
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
|
||||
Game game = controller.getModel();
|
||||
handler.notifyMiniModel(new MiniModel(
|
||||
game.getSlotMap(),
|
||||
game.getOrderLogicCard(),
|
||||
game.getCurrentState(),
|
||||
game.getPlayers(),
|
||||
game.getAvailableTotems(),
|
||||
game.getUpperListTribeCards(), game.getLowerListTribeCards(), game.getUpperListBuilding(), game.getLowerListBuilding(), game.getDisconnectedPlayers().entrySet().stream().filter(Map.Entry::getValue).map(x -> x.getKey().getUserName()).collect(Collectors.toCollection(ArrayList::new))
|
||||
));
|
||||
|
||||
Thread thread = new Thread(handler);
|
||||
thread.start();
|
||||
|
||||
clientHandlers.add(handler);
|
||||
connectedPlayers++;
|
||||
actionQueue.add(new ReconnectPlayer(username));
|
||||
|
||||
} else {
|
||||
|
||||
eventAddPlayer.setErrorType(ErrorType.USER_ALREADY_CONNECTED);
|
||||
eventAddPlayer.setIsError(true);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
clientSocket.close();
|
||||
System.out.println("Invalid parameters. Connection terminated.");
|
||||
}
|
||||
ClientHandler handler = createAndRegisterHandler(
|
||||
username, clientSocket, clientSend, clientReceive);
|
||||
new Thread(handler).start();
|
||||
} else if (!playerList.get(username)) {
|
||||
// reconnect a previously disconnected player
|
||||
playerList.put(username, true);
|
||||
System.out.println("Reconnected player: " + username);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
ClientHandler handler = createAndRegisterHandler(
|
||||
username, clientSocket, clientSend, clientReceive);
|
||||
Game game = controller.getModel();
|
||||
handler.notifyMiniModel(new MiniModel(
|
||||
game.getSlotMap(), game.getOrderLogicCard(),
|
||||
game.getCurrentState(), game.getPlayers(),
|
||||
game.getAvailableTotems(),
|
||||
game.getUpperListTribeCards(), game.getLowerListTribeCards(),
|
||||
game.getUpperListBuilding(), game.getLowerListBuilding(),
|
||||
disconnectedUsernames(game)));
|
||||
new Thread(handler).start();
|
||||
actionQueue.add(new ReconnectPlayer(username));
|
||||
} else {
|
||||
eventAddPlayer.setErrorType(ErrorType.USER_ALREADY_CONNECTED);
|
||||
eventAddPlayer.setIsError(true);
|
||||
clientSend.writeObject(eventAddPlayer);
|
||||
clientSocket.close();
|
||||
System.out.println("Invalid parameters. Connection terminated.");
|
||||
}
|
||||
}
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
@@ -340,7 +281,7 @@ public class TCPServer {
|
||||
*/
|
||||
public void notifyAll(NetworkEvent event) {
|
||||
clientHandlers.forEach(h -> {
|
||||
if (!event.getIsError() || event.getUsername().equals(h.getUsername())) {
|
||||
if (!event.isError() || event.getUsername().equals(h.getUsername())) {
|
||||
h.notifyEvent(event);
|
||||
}
|
||||
});
|
||||
@@ -354,4 +295,28 @@ public class TCPServer {
|
||||
public void notifyAll(MiniModel model) {
|
||||
clientHandlers.forEach(h -> h.notifyMiniModel(model));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ClientHandler}, registers it in {@code pendingHeartbeat}
|
||||
* and {@code clientHandlers}, but does NOT start its thread — callers start
|
||||
* the thread after any extra setup (e.g. sending a model snapshot).
|
||||
*/
|
||||
private ClientHandler createAndRegisterHandler(String username,
|
||||
Socket socket,
|
||||
ObjectOutputStream out,
|
||||
ObjectInputStream in) {
|
||||
ClientHandler handler = new ClientHandler(
|
||||
username, socket, out, in, clientHandlers, playerList, actionQueue);
|
||||
pendingHeartbeat.put(username, handler);
|
||||
clientHandlers.add(handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
/** Returns usernames of players currently marked as disconnected in the given game. */
|
||||
private static ArrayList<String> disconnectedUsernames(Game game) {
|
||||
return game.getDisconnectedPlayers().entrySet().stream()
|
||||
.filter(Map.Entry::getValue)
|
||||
.map(e -> e.getKey().getUserName())
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user