diff --git a/.gitignore b/.gitignore
index e44eabc0..5fa2958d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# Match files
+*.match
+
# Compiled class file
*.class
@@ -34,3 +37,4 @@ target/
.project
src/main/resources/graphics/
+bin/
diff --git a/deliveries/JAR/GUI.jar b/deliveries/JAR/GUI.jar
new file mode 100644
index 00000000..8d651521
Binary files /dev/null and b/deliveries/JAR/GUI.jar differ
diff --git a/deliveries/JAR/README.md b/deliveries/JAR/README.md
new file mode 100644
index 00000000..ff70af45
--- /dev/null
+++ b/deliveries/JAR/README.md
@@ -0,0 +1,30 @@
+# Eseguire i JAR
+## Jar del server
+
+```bash
+java -jar Server.jar [RMI Port] [TCP Port]
+```
+`RMI Port` e `TCP Port` sono opzionali, se non specificate assumono rispettivamente i valori `2222` e `9999`.
+
+## Jar della TUI
+
+```bash
+java -jar TUI.jar
+```
+Per eseguire la TUI e' consigliato utilizzare un nerd font.
+
+## Jar della GUI
+```bash
+java -jar GUI.jar
+```
+
+# Compilare i JAR
+1. (Opzionale, senza la GUI non funziona) Scaricare la cartella risorse da [qui](https://drive.google.com/file/d/15CvIqbfmjXKmvkwzr2-L_PMIKgQnIvA3/view?usp=sharing).
+2. Estrarre le immagini nella cartella `src/main/resources/images`
+3. Eseguire il comando:
+
+```bash
+./mvnw clean package
+```
+
+I file risultanti si troveranno nella cartella target.
\ No newline at end of file
diff --git a/deliveries/JAR/Server.jar b/deliveries/JAR/Server.jar
new file mode 100644
index 00000000..6fdf486b
Binary files /dev/null and b/deliveries/JAR/Server.jar differ
diff --git a/deliveries/JAR/TUI.jar b/deliveries/JAR/TUI.jar
new file mode 100644
index 00000000..1e79c8e3
Binary files /dev/null and b/deliveries/JAR/TUI.jar differ
diff --git a/deliveries/Javadoc/allclasses-index.html b/deliveries/Javadoc/allclasses-index.html
new file mode 100644
index 00000000..6a77d058
--- /dev/null
+++ b/deliveries/Javadoc/allclasses-index.html
@@ -0,0 +1,561 @@
+
+
+
Every time a socket gets accepted by the TCP server, a new ClientListener will be created with
+ it, and it will:
+
+ Acquire the client's username
+ Make the client (which is still not a Player) choose/create a Match to join
+ Create its PlayerControllerTCP, which will also make him join such Match
+ Listen for any message received and, execute the corresponding action.
This is a temporary class, used to have all the logic related to deck creation in a single place, so that
+ when it will be implemented correctly we know where to modify it
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Subclass of MatchObserver supposed to perform routine actions, which means actions that are to be
+ performed when there's a state transition, BUT that are not related to a specific player.
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
+Starting from the Overview page, you can browse the documentation using the links in each page, and in the navigation bar at the top of each page. The Index and Search box allow you to navigate to specific declarations and summary pages, including: All Packages, All Classes and Interfaces
+
+
Search
+
You can search for definitions of modules, packages, types, fields, methods, system properties and other terms defined in the API. These items can be searched using part or all of the name, optionally using "camelCase" abbreviations, or multiple search terms separated by whitespace. Some examples:
+The following sections describe the different kinds of pages in this collection.
+
+
Overview
+
The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.
+
+
+
Package
+
Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain the following categories:
+
+
Interfaces
+
Classes
+
Enum Classes
+
Exception Classes
+
Annotation Interfaces
+
+
+
+
Class or Interface
+
Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a declaration and description, member summary tables, and detailed member descriptions. Entries in each of these sections are omitted if they are empty or not applicable.
+
+
Class Inheritance Diagram
+
Direct Subclasses
+
All Known Subinterfaces
+
All Known Implementing Classes
+
Class or Interface Declaration
+
Class or Interface Description
+
+
+
+
Nested Class Summary
+
Enum Constant Summary
+
Field Summary
+
Property Summary
+
Constructor Summary
+
Method Summary
+
Required Element Summary
+
Optional Element Summary
+
+
+
+
Enum Constant Details
+
Field Details
+
Property Details
+
Constructor Details
+
Method Details
+
Element Details
+
+
Note: Annotation interfaces have required and optional elements, but not methods. Only enum classes have enum constants. The components of a record class are displayed as part of the declaration of the record class. Properties are a feature of JavaFX.
+
The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+
+
+
Other Files
+
Packages and modules may contain pages with additional information related to the declarations nearby.
+
+
+
Use
+
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the USE link in the navigation bar.
+
+
+
Tree (Class Hierarchy)
+
There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. Classes are organized by inheritance structure starting with java.lang.Object. Interfaces do not inherit from java.lang.Object.
+
+
When viewing the Overview page, clicking on TREE displays the hierarchy for all packages.
+
When viewing a particular package, class or interface page, clicking on TREE displays the hierarchy for only that package.
+
+
+
+
Serialized Form
+
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to those who implement rather than use the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See Also" section of the class description.
+
+
+
All Packages
+
The All Packages page contains an alphabetic index of all packages contained in the documentation.
+
+
+
All Classes and Interfaces
+
The All Classes and Interfaces page contains an alphabetic index of all classes and interfaces contained in the documentation, including annotation interfaces, enum classes, and record classes.
+
+
+
Index
+
The Index contains an alphabetic index of all classes, interfaces, constructors, methods, and fields in the documentation, as well as summary pages such as All Packages, All Classes and Interfaces.
+
+
+
+This help file applies to API documentation generated by the standard doclet.
+
+
Every time a socket gets accepted by the TCP server, a new ClientListener will be created with
+ it, and it will:
+
+ Acquire the client's username
+ Make the client (which is still not a Player) choose/create a Match to join
+ Create its PlayerControllerTCP, which will also make him join such Match
+ Listen for any message received and, execute the corresponding action.
This is a temporary class, used to have all the logic related to deck creation in a single place, so that
+ when it will be implemented correctly we know where to modify it
Tries to get the player's secret objectives pair (from which he will have to choose one),
+ unless there was a WrongTurnException or a WrongStateException, in which case
+ a new ErrorMessage is sent with the exception content
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Ask a player to choose a card to play, the side on which the card should be played, and the
+ coordinates in which the card should be played; finally trying to actually play the card.
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Subclass of MatchObserver supposed to perform routine actions, which means actions that are to be
+ performed when there's a state transition, BUT that are not related to a specific player.
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Modifies the current player according to the next turn: if it's the first turn, the current player is the first
+ one in the players List, the turn order then follows the players List order, in a circular way.
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Instantiates the internal Player with the given username and sets the internal Match reference to
+ the given one, furthermore add the new Player instance to the match and subscribe this class
+ instance to the match observers.
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
Instantiates the internal Player with the given username and sets the internal Match
+ reference to the given one, add the new Player instance to the match and subscribe this class
+ instance to the match observers.
This method call is allowed by this class instances if and only if the match hasn't started yet
+ and the initial card side choosing has already finished.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
Triggers the transition in Match from the current state to the next one, this means changing the current Match's
+ MatchState instance to a new one, having the same static type (MatchState) but a different dynamic type (a subclass
+ of MatchState).
Transitions to:
+ - AfterMoveState if the match has already started;
+ - ChooseInitialSideState if the initial card side choosing hasn't finished yet;
+ - ChooseSecretObjectiveState if the match hasn't started yet and the initial card side choosing has
+ already finished;
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Notifies other players that someone is choosing the secret objective. They should not know from
+ which objective he is choosing, so they are not passed.
Represents the current match macro-state from the client point of view.
+ This is not supposed to substitute MatchState, but rather make it easier for clients
+ to keep track of the current state of match, which is to say whether it's unused (LOBBY),
+ in WaitState (WAIT_STATE) or being played (MATCH_STATE).
+
+
+
+
+
+
+
Nested Class Summary
+
+
Nested classes/interfaces inherited from class java.lang.Enum
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. All components in this record class are compared with Objects::equals(Object,Object).
public class GraphicalApplication
+extends javafx.application.Application
+
Class from which the FXML application is run, so it's entry point for the user.
+ Apart from making use of FXML instances and methods, it interacts massively with GraphicalViewGUI.
+
+
+
+
+
+
+
Nested Class Summary
+
+
Nested classes/interfaces inherited from class javafx.application.Application
Notifies other players that someone is choosing the secret objective. They should not know from
+ which objective he is choosing, so they are not passed.
Handles the receipt of a broadcast message from a user who's not the current client.
+ For this purpose the method confirmSubmitBroadcastMessage(...) can be used.
+
+
Parameters:
+
senderUsername - The username of the player that sent the message
Handles the receipt of a private message from a user who's not the current client.
+ For this purpose the method confirmSubmitPrivateMessage(...) can be used.
+
+
Parameters:
+
senderUsername - The username of the player that sent the message
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. Reference components are compared with Objects::equals(Object,Object); primitive components are compared with '=='.
Ask a player to choose a card to play, the side on which the card should be played, and the
+ coordinates in which the card should be played; finally trying to actually play the card.
Adds to the list of player with objectives the last player who chose his secret objective.
+ This is used to determine whether the player currently playing has already chosen secret
+ objective (and so should play a regular turn) or not.
Ask a player to choose a card to play, the side on which the card should be played, and the
+ coordinates in which the card should be played; finally trying to actually play the card.
Prints indexes for available spots, during the placing card phase.
+
+
Parameters:
+
validPlaces - map FROM board's coordinates where the hypotetic card would be TO a pair,
+ consisting of a number (the index) and the corner where future card would be linked
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerController instance.
+ It represents an abstract layer, being implemented by: NetworkHandlerRMI and NetworkHandlerTCP.
Notifies that the match has just started.
+ Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ state.
playersUsernamesAndPawns - Map that matches each pawn color to the corresponding player's username
+
playersHands - Map that matches each player's username to the corresponding List of cards in the hand
+
visibleObjectives - Pair of objectives visible to all players
+
visiblePlayableCards - Map having as values the visible common cards (the keys are just indexes).
+
decksTopReigns - Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ the first one is the gold deck one, the second one the resource deck one
Notifies that the match has resumed.
+ Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ state.
playersUsernamesAndPawns - Map that matches each pawn color to the corresponding player's username
+
playersHands - Map that matches each player's username to the corresponding List of cards in the hand
+
visibleObjectives - Pair of objectives visible to all players
+
visiblePlayableCards - Map having as values the visible common cards (the keys are just indexes).
+
decksTopReigns - Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ the first one is the gold deck one, the second one the resource deck one
+
secretObjective - Secret objective of the current player
+
availableResources - Available resources of all the players
Notifies that someone (it may or may not be the receiving View instance) has drawn a pair of secret objectives.
+ Mind that the objectives are not passed as arguments, since they are secret to all players but the one receiving
+ them. The one meant to receive them receives this message too but obtain the objectives through the
+ giveSecretObjective() method.
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
+
+
+
+
+
+
+
Field Summary
+
+
Fields inherited from class it.polimi.ingsw.client.network.NetworkHandler
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
+
+
+
+
+
+
+
Field Summary
+
+
Fields inherited from class it.polimi.ingsw.client.network.NetworkHandler
public interface RemoteViewInterface
+extends Remote
+
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
+ Since it's a remote interface, all the methods here defined are meant to notify the occurrence of an event to the remote
+ object. Given this, all methods also contain some parameters specific to the happened event.
+ For security reasons, each method doesn't expose to the receiving view important objects (e.g. Player), but
+ rather values representing them (e.g. Player's username).
Notifies that the match has just started.
+ Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ state.
+
+
Parameters:
+
playersUsernamesAndPawns - Map that matches each pawn color to the corresponding player's username
+
playersHands - Map that matches each player's username to the corresponding List of cards in the hand
+
visibleObjectives - Pair of objectives visible to all players
+
visiblePlayableCards - Map having as values the visible common cards (the keys are just indexes).
+
decksTopReigns - Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ the first one is the gold deck one, the second one the resource deck one
+
Throws:
+
RemoteException - If the remote object is considered not to be reachable anymore and cannot return as usual
Notifies that the match has resumed.
+ Furthermore, gives to the receiving object all the information (parameters) needed to restore to the current match
+ state.
+
+
Parameters:
+
playersUsernamesAndPawns - Map that matches each pawn color to the corresponding player's username
+
playersHands - Map that matches each player's username to the corresponding List of cards in the hand
+
visibleObjectives - Pair of objectives visible to all players
+
visiblePlayableCards - Map having as values the visible common cards (the keys are just indexes).
+
decksTopReigns - Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ the first one is the gold deck one, the second one the resource deck one
+
secretObjective - Secret objective of the current player
+
availableResources - Available resources of all the players
+
placedCards - Placed cards of all the players
+
playerPoints - Points of all the players
+
currentPlayer - The current player
+
drawPhase - If the match is resumed in draw phase
+
Throws:
+
RemoteException - If the remote object is considered not to be reachable anymore and cannot return as usual
Notifies that someone (it may or may not be the receiving View instance) has drawn a pair of secret objectives.
+ Mind that the objectives are not passed as arguments, since they are secret to all players but the one receiving
+ them. The one meant to receive them receives this message too but obtain the objectives through the
+ giveSecretObjective() method.
+
+
Parameters:
+
someoneUsername - The username of the player who has drawn the card
+
Throws:
+
RemoteException - If the remote object is considered not to be reachable any more and cannot return as usual
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Class used by a generic client to receive from and transmit to a remote Server instance and a
+ remote PlayerControllerRMI instance using the RMI protocol.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ by message listener for TCP.
Class used by a generic client to receive from and transmit to a remote Server instance
+ and a remote PlayerControllerTCP instance using the TCP protocol.
Controller for a match player, the only agent needing a view and so a controller in this
+ application. This class subclasses instances are given (in RMI case) / reachable (in TCP case) on
+ the network and collected by a corresponding view (RMI view or TCP view); then this class commits
+ its two subclasses PlayerControllerRMI and PlayerControllerTCP to implement all
+ the methods needed by a generic view to play in a match. This class implements
+ MatchObserver since its instances subscribe themselves to a Match, as mentioned in
+ PlayerController(String, Match); this is needed to allow this class to behave as a
+ bridge between a view and a match.
Instantiates the internal Player with the given username and sets the internal Match reference to
+ the given one, furthermore add the new Player instance to the match and subscribe this class
+ instance to the match observers.
publicPlayerController(String username,
+ Match match)
+
Instantiates the internal Player with the given username and sets the internal Match reference to
+ the given one, furthermore add the new Player instance to the match and subscribe this class
+ instance to the match observers.
+
+
Parameters:
+
username - The username of the new player of the Match
+
match - The match to which this PlayerClass must pertain
Subclass of PlayerController that implements its abstract methods through RMI interactions.
+ Each instance of this class is supposed to be sent through Server.joinMatch(String, String))
+ to an RMI View, this latter will then send its View instance to the PlayerController object, calling
+ registerView(RemoteViewInterface) on it.
+
+
+
+
+
+
+
Field Summary
+
+
Fields inherited from class it.polimi.ingsw.controllers.PlayerController
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
+
+
Parameters:
+
username - The username of the new player of the Match
+
match - The match to which this PlayerClass must pertain
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
+ It's used by a remote View having this class object to send itself over RMI to the PlayerControllerRMI
+ instance.
+ Note that this method is supposed to be called by a view.
Draws an initial card for the player. Since this is done through RMI, it just involves a call to
+ Player.drawInitialCard().
+ Note that this method is supposed to be called by a view.
Communicates the chosen initial card side. Since this is done through RMI, it just involves a call to
+ Player.chooseInitialCardSide(Side).
+ Note that this method is supposed to be called by a view.
Draws two secret objectives. Since this is done through RMI, it just involves a call to
+ Player.drawSecretObjectives().
+ Note that this method is supposed to be called by a view.
Communicates the chosen secret objective. Since this is done through RMI, it just involves a call to
+ Player.chooseSecretObjective(Objective).
+ Note that this method is supposed to be called by a view.
Plays a card. Since this is done through RMI, it just involves a call to
+ Player.playCard(Pair, PlayableCard, Side).
+ Note that this method is supposed to be called by a view.
Draws a card. Since this is done through RMI, it just involves a call to
+ Player.drawCard(DrawSource).
+ Note that this method is supposed to be called by a view.
Sends a broadcast in the chat. Since this is done through RMI, it just involves a call to
+ Player.sendBroadcastText(String).
+ Note that this method is supposed to be called by a view.
Sends a private message in the chat. Since this is done through RMI, it just involves a call to
+ Player.sendPrivateText(Player, String) )}.
+ Note that this method is supposed to be called by a view.
Notifies that someone has joined the match.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ the view about the current lobby information.
Notifies that someone has quit from the match.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
Notifies that someone has drawn its initial card.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ the view that it received an initial card.
Notifies that someone has chosen its initial card side.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
Notifies that someone has drawn two secret objectives.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ the view about the proposed objectives, the other views will just receive a notification about the player's username.
Notifies that someone has chosen the secret objective.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ The view will just receive `someone` username, no the objective.
Notifies that someone has played a card.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
Notifies that someone has drawn a card.
+ The replacement card is the one that has taken the place of the drawn one, it's needed since observers have to
+ know the reign of the new card on top of the decks.
+ Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
publicvoidsomeoneSentPrivateText(Player someone,
+ Player recipient,
+ String text)
+
Notifies that someone sent a private message to another user.
+ If the recipient is the current player, then the view is notified,
+ otherwise the message is ignored.
public interface PlayerControllerRMIInterface
+extends Remote
+
RMI interface used to declare all and only the methods callable on a remote PlayerControllerRMI instance implementing
+ this interface. Since it's a remote interface, all the methods here defined are meant to notify the occurrence of an
+ event to the remote object. Given this, all methods also contain some parameters specific to the happened event (e.g.
+ a user clicked the button to play a card on the GUI view, then playCard(Pair, PlayableCard, Side)) is called.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
+ It's used by a remote View having this class object to send itself over RMI to the PlayerControllerRMI
+ instance.
+
+
Parameters:
+
view - The View to save in the PlayerController internal state
Instantiates the internal Player with the given username and sets the internal Match
+ reference to the given one, add the new Player instance to the match and subscribe this class
+ instance to the match observers.
Tries to get the player's secret objectives pair (from which he will have to choose one),
+ unless there was a WrongTurnException or a WrongStateException, in which case
+ a new ErrorMessage is sent with the exception content
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Instantiates the internal Player with the given username and sets the internal Match
+ reference to the given one, add the new Player instance to the match and subscribe this class
+ instance to the match observers.
+
+
Parameters:
+
username - The username of the new player of the Match
+
match - The match to which this PlayerClass must pertain
+
io - The I/O handler to be attached to this instance
+
+
+
+
+
+
+
+
+
+
Method Details
+
+
+
+
matchStarted
+
publicvoidmatchStarted()
+
Notifies that the match has just started. Note that is supposed to be called by the match.
Notifies that someone has joined the match. Note that this method is supposed to be called by
+ a match, moreover the match calls this method on all the MatchObservers instance subscribed
+ to itself, then even the MatchObserver causing this event gets notified. If and only if the
+ PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ the view about the current lobby information.
Notifies that someone has quit from the match. Note that Match calls this method on all
+ MatchObservers instance subscribed to itself, then even the MatchObserver causing this event
+ gets notified.
Notifies that someone has drawn its initial card. Note that this method is supposed to be
+ called by a match, moreover the match calls this method on all the MatchObservers instance
+ subscribed to itself, then even the MatchObserver causing this event gets notified. If and
+ only if the PlayerController receiving this method call is the one linked to given `someone`,
+ it notifies the view that it received an initial card.
+
+
Parameters:
+
someone - The player instance that has drawn the card
Notifies that someone has chosen its initial card side. Note that this method is supposed to
+ be called by a match, moreover the match calls this method on all the MatchObservers instance
+ subscribed to itself, then even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The player instance that has chosen the side
+
side - The chosen initial card side
+
availableResources - The resources available at the moment to the player that set its initial card side
Notifies that someone has drawn two secret objectives. Note that this method is supposed to
+ be called by a match, moreover the match calls this method on all the MatchObservers instance
+ subscribed to itself, then even the MatchObserver causing this event gets notified. If and
+ only if the PlayerController receiving this method call is the one linked to given `someone`,
+ it notifies the view about the proposed objectives, the other views will just receive a
+ notification about the player's username.
+
+
Parameters:
+
someone - The player instance that has drawn the objectives
Notifies that someone has chosen the secret objective. Note that this method is supposed to
+ be called by a match, moreover the match calls this method on all the MatchObservers instance
+ subscribed to itself, then even the MatchObserver causing this event gets notified. The view
+ will just receive `someone` username, no the objective.
+
+
Parameters:
+
someone - The player instance that has chosen the secret objective
Notifies that someone has played a card. Note that this method is supposed to be called by a
+ match, moreover the match calls this method on all the MatchObservers instance subscribed to
+ itself, then even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The Player instance that has played a card
+
coords - The coordinates on which the card has been placed
Notifies that someone has drawn a card. The replacement card is the one that has taken the
+ place of the drawn one, it's needed since observers have to know the reign of the new card on
+ top of the decks. Note that this method is supposed to be called by a match, moreover the
+ match calls this method on all the MatchObservers instance subscribed to itself, then even
+ the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The Player instance that has drawn a card
+
source - The drawing source from which the card has been drawn
+
card - The card that has been drawn
+
replacementCard - The card that has replaced the drawn card, null if the draw source is
+ a deck
Notifies that someone sent a message in the public chat.
+
+
Parameters:
+
someone - The player that send the message
+
text - Content of the message
+
+
+
+
+
+
someoneSentPrivateText
+
publicvoidsomeoneSentPrivateText(Player someone,
+ Player recipient,
+ String text)
+
Notifies that someone sent a private message to another user. If the recipient is the current
+ player, then the view is notified, otherwise the message is ignored.
Tries to get the player's secret objectives pair (from which he will have to choose one),
+ unless there was a WrongTurnException or a WrongStateException, in which case
+ a new ErrorMessage is sent with the exception content
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Sets the internal View attribute to the given argument; if it has already been called, it won't
+ do anything, since it's call is allowed once per PlayerController object.
Register the given view as the one attached to the remote PlayerControllerRMI instance; if it has already been
+ called, it won't do anything, since it's call is allowed once per PlayerController object.
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
This method call is allowed by this class instances if and only if the match hasn't started yet
+ and the initial card side choosing has already finished.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
+
+
Parameters:
+
coord - the coordinates in which the card should be played
+
card - the card to check on
+
side - the side of the card (needed for requirement check)
+
Returns:
+
the outcome for the placement, which is valid only if all conditions are met
+
Throws:
+
CardException - if the card is not in the player's hand
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
+ Other methods serve the purpose of being called by MatchState subclasses in order to notify the change
+ of the current game state or trigger some changes in the match, such as setupBoards(...),
+ doStart(...), etc.
+ Few methods are called by the current player of the match, used to trigger a change in the match and so notify that
+ an event occurred, such as nextPlayer(...).
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Modifies the current player according to the next turn: if it's the first turn, the current player is the first
+ one in the players List, the turn order then follows the players List order, in a circular way.
Removes a player from the match, assuming the player is in the match.
+ Note: Called by the Controller when a player quits the match.
+
+
Parameters:
+
player - player to be removed from the match
+
+
+
+
+
+
isFull
+
publicbooleanisFull()
+
Verifies if the match is full, thus no more players can join.
+ Note: Used by the Controller
+
+
Returns:
+
true if the match is full, false otherwise
+
+
+
+
+
+
nextPlayer
+
protectedvoidnextPlayer()
+
Modifies the current player according to the next turn: if it's the first turn, the current player is the first
+ one in the players List, the turn order then follows the players List order, in a circular way.
+ Ex. 1st -> 2nd -> 3rd ---> 1st -> 2nd etc.
+ Note: Called by NextTurnState every time a new turn starts.
+
+
+
+
+
isFinished
+
publicbooleanisFinished()
+
Verifies if the match is finished.
+ Note: Called by the Controller and NextTurnState.
+
+
Returns:
+
true if the match is finished, false otherwise
+
+
+
+
+
+
doInitialTurnFinish
+
protectedvoiddoInitialTurnFinish()
+
Marks the initial turn as finished, assuming the initial turn hasn't finished yet.
+ Called by ChooseInitialCardState once the initial turn is finished.
+
+
+
+
+
isInitialTurnFinished
+
publicbooleanisInitialTurnFinished()
+
Verifies if the initial turn is finished.
+ Note: Called by NextTurnState.
+
+
Returns:
+
true if the initial turn is finished, false otherwise
+
+
+
+
+
+
doStart
+
protectedvoiddoStart()
+
Marks the match as started, assuming the match hasn't started yet.
+ Note: Called by ChooseSecretObjectiveState once the match is ready to start.
+
+
+
+
+
isStarted
+
publicbooleanisStarted()
+
Verifies if the match is started.
+ Note: Called by NextTurnState to check when to effectively start the match.
Checks that the given objective is one of the proposed ones to the current player
+ and put the discarded objective back in the objectives deck.
+ Note: Called by the current player
+
+
Parameters:
+
objective - the accepted objective by the player (NOT the discarded one)
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
+ Note: Called by the current player.
+
+
Parameters:
+
coords - coordinates in which to place the card
+
card - card to place
+
side - side of the card to be placed
+
Throws:
+
WrongStateException - if called while in a state that doesn't allow making moves
+
WrongChoiceException - if the move is not allowed (placement not allowed, or not enough resources, or card
+ not in player's hand)
Draws a card from the passed source. If the caller wants to draw one of the four visible cards, a rule is applied:
+ the first and second visible cards (FIRST/SECOND_VISIBLE) will be substituted by a gold card if possible, if not,
+ by a resource card, if not again, they will be null (then not substituted); the first and second visible cards
+ (THIRD/FOURTH_VISIBLE) will be substituted by a resource card if possible, if not, by a gold card, if not again,
+ they will be null (then not substituted).
+ Note: Called by the current player.
+
+
Parameters:
+
source - the source to draw a card from
+
Returns:
+
the card drawn
+
Throws:
+
WrongStateException - if called while in a state that doesn't allow making moves
Getter for the final ranking of players. Return a valid result if and only if called when the match is in the
+ FinalState, so it's finished.
+
+
Returns:
+
Players ranking of the match at the end of it; the List order represents the ranking order, the Boolean
+ represent if the related player is a winner; this is needed since the match can end in a tie, in such case the
+ first two/three players of the List will have a True flag
Getter for the cards back on the top of the decks (i.e. those visible top cards).
+ Both of them always contain just a reign.
+
+
Returns:
+
Pair of two reign Symbol (see Symbol.getReigns()), the first one regards the
+ top card of gold cards deck, the second one regards the top card of resource cards deck
+
+
+
+
+
+
getMaxPlayers
+
publicintgetMaxPlayers()
+
Getter for the maximum number of player for the match
Removes the given MatchObserver to those observers notified on match events.
+
+
Parameters:
+
observer - The observer to be removed
+
+
+
+
+
+
notifyMatchStart
+
protectedvoidnotifyMatchStart()
+
Notifies all match observers that the match has started.
+ It's called by WaitState methods after the match setup, that's why it needs to be protected.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Notifies that someone has joined the match.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
Notifies that someone has quit from the match.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
Notifies that someone has drawn its initial card.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The player instance that has drawn the card
Notifies that someone has chosen its initial card side.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The player instance that has chosen the side
+
side - The chosen initial card side
+
availableResources - The resources available at the moment to the player that set its initial card side
Notifies that someone has drawn two secret objectives.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The player instance that has drawn the objectives
Notifies that someone has chosen the secret objective.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The player instance that has chosen the secret objective
Notifies that someone has played a card.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The Player instance that has played a card
+
coords - The coordinates on which the card has been placed
Notifies that someone has drawn a card.
+ The replacement card is the one that has taken the place of the drawn one, it's needed since observers have to
+ know the reign of the new card on top of the decks.
+ Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ even the MatchObserver causing this event gets notified.
+
+
Parameters:
+
someone - The Player instance that has drawn a card
+
source - The drawing source from which the card has been drawn
+
card - The card that has been drawn
+
replacementCard - The card that has replaced the drawn card
public abstract class MatchState
+extends Object
+implements Serializable
+
Represents an appendix of Match.
+ Match fully delegates to this class, through composition, the role of keeping track of the current game state: it
+ throws exceptions when a Match method is called while being in the wrong state and triggers some Match behavior
+ (calling match.someMethod(...)) when a certain transition has occurred.
+ Each method is supposed to be overridden by a subclass of MatchState if and only if it is allowed to be called from
+ that specific subclass; if not overridden, the MatchState version of the method will be called and thus an exception
+ will be thrown.
Triggers the transition in Match from the current state to the next one, this means changing the current Match's
+ MatchState instance to a new one, having the same static type (MatchState) but a different dynamic type (a subclass
+ of MatchState).
Triggers the transition in Match from the current state to the next one, this means changing the current Match's
+ MatchState instance to a new one, having the same static type (MatchState) but a different dynamic type (a subclass
+ of MatchState).
Checks dynamically if a player can be added in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a player can be removed in the current state, otherwise it forces the Match to go to the
+ FinalState.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState instance from which it's called (just WaitState overrides it).
Checks dynamically if an initial card can be drawn in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a player can choose the initial card side in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a player can make a move in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a player can draw a playable card in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a secret objective can be proposed to the current player in the current state, otherwise
+ throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Checks dynamically if a player can choose their secret objective in the current state, otherwise throws an exception.
+ The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ overridden by the MatchState object from which it's called.
Subclass of MatchState. This is the state in which the match decides whether it must give initial cards, give
+ secret objectives, or wait for someone to play a card, according to the current match flow.
This method call is allowed by this class instances if and only if the match hasn't started yet
+ and the initial card side choosing has already finished.
Transitions to:
+ - AfterMoveState if the match has already started;
+ - ChooseInitialSideState if the initial card side choosing hasn't finished yet;
+ - ChooseSecretObjectiveState if the match hasn't started yet and the initial card side choosing has
+ already finished;
+
+
+
+
+
+
Methods inherited from class it.polimi.ingsw.gamemodel.MatchState
This method call is allowed by this class instances if and only if the match hasn't started yet
+ and the initial card side choosing has already finished.
WrongStateException - If called when the initial card side choosing has already finished.
+
+
+
+
+
+
transition
+
publicvoidtransition()
+
Transitions to:
+ - AfterMoveState if the match has already started;
+ - ChooseInitialSideState if the initial card side choosing hasn't finished yet;
+ - ChooseSecretObjectiveState if the match hasn't started yet and the initial card side choosing has
+ already finished;
Every player has a secret objective, and at the start of the game two objectives common to every
+ player get randomly chosen. The objective asks for a certain requirement to be satisfied and gives points only when
+ the game ends, and does not stack on itself (e.g. if an objective requires three feathers and a player has 6, he will receive the points only once)
turn - the turn said card is played. Needed to know which card covers which, since a card played in a certain turn will
+ always cover one played before
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Class that represents every kind of card that can be played during the game.
+ All these cards have at least a side (the back) that does not require any
+ resource to be played.
Constructor for PlayableCard. Since the only common aspects are that they
+ have a reign and that the back is made up of
+ only full corners (no resources) and the center gives a resource of their
+ reign
Represents each in-game user, so acts also as a gateway receiving input by the Controller.
+ It's also responsible for the board's logic, which is a slice of the game logic.
Empty class constructor. The actual constructor resides in the subclasses, but this method is used to know
+ what to expect when creating a new Requirement, allowing the use of polymorphism
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Contains all the possible symbols a corner can store or a multiplier can have.
+ Besides the basic 4 factions and 3 symbols, there is also EMPTY_CORNER, which represents a corner without the
+ possibility of placing another card on top of it (missing slot), FULL_CORNER which represents a valid corner without any symbol and
+ CORNER_OBJ which represents a QuantityRequirement in which the multiplier is how many corners the card covered
+
+
+
+
+
+
+
Nested Class Summary
+
+
Nested classes/interfaces inherited from class java.lang.Enum
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Subclass of MatchState. This is the state in which the match is when accepting new players or them leaving,
+ that is to say: before the match is full and so it starts.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Instantiates the internal Player with the given username and sets the internal Match reference to
+ the given one, furthermore add the new Player instance to the match and subscribe this class
+ instance to the match observers.
Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ add the new Player instance to the match and subscribe this class instance to the match observers.
Instantiates the internal Player with the given username and sets the internal Match
+ reference to the given one, add the new Player instance to the match and subscribe this class
+ instance to the match observers.
Subclass of MatchObserver supposed to perform routine actions, which means actions that are to be
+ performed when there's a state transition, BUT that are not related to a specific player.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Represents the match played by Player instances, therefore implements a slice of game logic
+ using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
Interface to be implemented by any class that wants to be an observer of Match, so wants to be able to
+ get notified when an event occurs in a match (to which it's subscribed).
public final class ChooseInitialCardSideMessage
+extends ActionMessage
+
The action communicates the player's choice of the initial card's side. It can only happen before the first turn of the game.
+ If the action is successful, SomeoneDrewCardMessage response is sent to every client.
public final class ChooseSecretObjectiveMessage
+extends ActionMessage
+
The action communicates the intention of a player to choose his secret objective. It can only happen before the first turn of the game.
+ If the action is successful, a SomeoneChoseSecretObjectiveMessage response is sent to every client.
public final class DrawCardMessage
+extends ActionMessage
+
The action communicates the intention of a player to draw a card. It can only happen during the player's own turn.
+ If the action is successful, a SomeoneDrewInitialCardMessage response is sent to every client.
public final class DrawInitialCardMessage
+extends ActionMessage
+
It communicates the intention of a player to draw the initial card. It can only happen before the first turn of the game.
+ - If the action is successful, a SomeoneDrewInitialCardMessage response is sent to every client.
public final class DrawSecretObjectivesMessage
+extends ActionMessage
+
It communicates the intention of a player to draw the (2) secret objectives. It can only happen before the first turn of the game.
+ If the action is successful, a SomeoneDrewSecretObjectivesMessage response is sent to every client.
public final class PlayCardMessage
+extends ActionMessage
+
The action communicates the intention of the player to place a card on its board. It can only happen during the player's own turn.
+ If the action is successful, a SomeonePlayedCardMessage response is sent to every client.
a list containing a JsonObject for each match with properties:
+ name - name of the match
+ maxPlayers - maximum number of players
+ joinedPlayers - number of players in the match
a list of JSONObject with properties
+ username (String) - username of the current player
+ points (Integer) - total number of points gained during the match
+ winner (boolean) - if the current player is also the winner of the game
Calculates the needed parameters given some information from the match
+
+
Parameters:
+
objectives - Pair containing the two visible objectives
+
cards - Map that for each visible draw source maps the visible card
+
deckReigns - Pair containing the reign of the two visible cards on top of the deck.
+ The first is for the Golds deck, while the second for the resources deck.
Class constructor. It will open a ServerSocket on the specified port
+
+
Parameters:
+
port - the port on which the server should be started
+
server - the Server object that contains all the Match
+ objects
+
+
+
+
+
+
+
+
+
+
Method Details
+
+
+
+
listen
+
publicvoidlisten()
+
Main loop. Until the ServerSocket is not closed, it will listen for
+ any Socket that tries to connect and accept them. Finally, it will
+ start a new ClientListener with it
Instantiates the internal Player with the given username and sets the internal Match
+ reference to the given one, add the new Player instance to the match and subscribe this class
+ instance to the match observers.
Every time a socket gets accepted by the TCP server, a new ClientListener will be created with
+ it, and it will:
+
+ Acquire the client's username
+ Make the client (which is still not a Player) choose/create a Match to join
+ Create its PlayerControllerTCP, which will also make him join such Match
+ Listen for any message received and, execute the corresponding action.
Subclass of MatchObserver supposed to perform routine actions, which means actions that are to be
+ performed when there's a state transition, BUT that are not related to a specific player.
+ It's current main function is to serialize the match from which it receives notifications.
The server class of this application. It's appointed with managing remote interactions with clients
+ (NetworkHandler) before the match starts, after that PlayerController will ensure the
+ communication.
+ To be specific, it stores all the Match instances available (not full) or being played at the moment,
+ creates them when requested by clients and restores them from disk (since periodically serialized) after a
+ Server crash.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
+ This method is called just by remote NetworkHandlerRMI instances.
WrongNameException - If the chosen player username doesn't meet the alphanumerical criteria
+
+
+
+
+
+
ping
+
publicbooleanping()
+
Pings the server in order to perceive if the connection is still alive and working.
+ Always return true, since the false is implicit in returning a RemoteException
+ when the connection is not working anymore.
public interface ServerRMIInterface
+extends Remote
+
RMI interface used to declare all and only the methods callable on a remote Server instance implementing this
+ interface by a client.
+ For security reasons, each method doesn't expose to the receiving view important objects (e.g. Match), but
+ rather values representing them (e.g. Match unique name).
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ an instance of its PlayerControllerRMI, to start communicating through it with the match.
+
+
Parameters:
+
matchName - The unique name of the match to join to
+
username - The chosen player username
+
Returns:
+
An instance of PlayerControllerRMI, used exclusively by the calling view
+
Throws:
+
RemoteException - If the remote server is considered not to be reachable any more and cannot return as usual
+
ChosenMatchException - If the chosen match is either already full or doesn't exist
Subclass of MatchObserver supposed to perform routine actions, which means actions that are to be
+ performed when there's a state transition, BUT that are not related to a specific player.
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. Reference components are compared with Objects::equals(Object,Object); primitive components are compared with '=='.
Singleton that represents a collection of all cards actually existing in the Game, so only those used
+ in the Match instances.
+ It's appointed to initialise them with instances to be de-serialized from a file and make them available
+ through getters.
This is a temporary class, used to have all the logic related to deck creation in a single place, so that
+ when it will be implemented correctly we know where to modify it
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. Reference components are compared with Objects::equals(Object,Object); primitive components are compared with '=='.
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. All components in this record class are compared with Objects::equals(Object,Object).
Returns a string representation of this record class. The representation contains the name of the class, followed by the name and value of each of the record components.
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. All components in this record class are compared with Objects::equals(Object,Object).
Returns the enum constant of this class with the specified name.
+The string must match exactly an identifier used to declare an
+enum constant in this class. (Extraneous whitespace characters are
+not permitted.)
+
+
Parameters:
+
name - the name of the enum constant to be returned.
Tries to place a card on the player's board, unless there was a WrongStateException,
+ in which case a new ErrorMessage is sent with the exception content
Makes the chosen move on the board of the current player (known because of the internal Match state);
+ in particular, checks if the placement is valid, then places the card on the player's board and add points
+ to the player.
Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ the given coordinates must be valid, and if the card has a requirement it must be met
This is a temporary class, used to have all the logic related to deck creation in a single place, so that
+ when it will be implemented correctly we know where to modify it
+
+
diff --git a/deliveries/Javadoc/legal/ADDITIONAL_LICENSE_INFO b/deliveries/Javadoc/legal/ADDITIONAL_LICENSE_INFO
new file mode 100644
index 00000000..ff700cd0
--- /dev/null
+++ b/deliveries/Javadoc/legal/ADDITIONAL_LICENSE_INFO
@@ -0,0 +1,37 @@
+ ADDITIONAL INFORMATION ABOUT LICENSING
+
+Certain files distributed by Oracle America, Inc. and/or its affiliates are
+subject to the following clarification and special exception to the GPLv2,
+based on the GNU Project exception for its Classpath libraries, known as the
+GNU Classpath Exception.
+
+Note that Oracle includes multiple, independent programs in this software
+package. Some of those programs are provided under licenses deemed
+incompatible with the GPLv2 by the Free Software Foundation and others.
+For example, the package includes programs licensed under the Apache
+License, Version 2.0 and may include FreeType. Such programs are licensed
+to you under their original licenses.
+
+Oracle facilitates your further distribution of this package by adding the
+Classpath Exception to the necessary parts of its GPLv2 code, which permits
+you to use that code in combination with other independent modules not
+licensed under the GPLv2. However, note that this would not permit you to
+commingle code under an incompatible license with Oracle's GPLv2 licensed
+code by, for example, cutting and pasting such code into a file also
+containing Oracle's GPLv2 licensed code and then distributing the result.
+
+Additionally, if you were to remove the Classpath Exception from any of the
+files to which it applies and distribute the result, you would likely be
+required to license some or all of the other code in that distribution under
+the GPLv2 as well, and since the GPLv2 is incompatible with the license terms
+of some items included in the distribution by Oracle, removing the Classpath
+Exception could therefore effectively compromise your ability to further
+distribute the package.
+
+Failing to distribute notices associated with some files may also create
+unexpected legal consequences.
+
+Proceed with caution and we recommend that you obtain the advice of a lawyer
+skilled in open source matters before removing the Classpath Exception or
+making modifications to this package which may subsequently be redistributed
+and/or involve the use of third party software.
diff --git a/deliveries/Javadoc/legal/ASSEMBLY_EXCEPTION b/deliveries/Javadoc/legal/ASSEMBLY_EXCEPTION
new file mode 100644
index 00000000..42966666
--- /dev/null
+++ b/deliveries/Javadoc/legal/ASSEMBLY_EXCEPTION
@@ -0,0 +1,27 @@
+
+OPENJDK ASSEMBLY EXCEPTION
+
+The OpenJDK source code made available by Oracle America, Inc. (Oracle) at
+openjdk.org ("OpenJDK Code") is distributed under the terms of the GNU
+General Public License version 2
+only ("GPL2"), with the following clarification and special exception.
+
+ Linking this OpenJDK Code statically or dynamically with other code
+ is making a combined work based on this library. Thus, the terms
+ and conditions of GPL2 cover the whole combination.
+
+ As a special exception, Oracle gives you permission to link this
+ OpenJDK Code with certain code licensed by Oracle as indicated at
+ https://openjdk.org/legal/exception-modules-2007-05-08.html
+ ("Designated Exception Modules") to produce an executable,
+ regardless of the license terms of the Designated Exception Modules,
+ and to copy and distribute the resulting executable under GPL2,
+ provided that the Designated Exception Modules continue to be
+ governed by the licenses under which they were offered by Oracle.
+
+As such, it allows licensees and sublicensees of Oracle's GPL2 OpenJDK Code
+to build an executable that includes those portions of necessary code that
+Oracle could not provide under GPL2 (or that Oracle has provided under GPL2
+with the Classpath exception). If you modify or add to the OpenJDK code,
+that new GPL2 code may still be combined with Designated Exception Modules
+if the new code is made subject to this exception by its copyright holder.
diff --git a/deliveries/Javadoc/legal/LICENSE b/deliveries/Javadoc/legal/LICENSE
new file mode 100644
index 00000000..8b400c7a
--- /dev/null
+++ b/deliveries/Javadoc/legal/LICENSE
@@ -0,0 +1,347 @@
+The GNU General Public License (GPL)
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to
+most of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must
+make sure that they, too, receive or can get the source code. And you must
+show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program proprietary.
+To prevent this, we have made it clear that any patent must be licensed for
+everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included
+without limitation in the term "modification".) Each licensee is addressed as
+"you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may
+at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such modifications
+or work under the terms of Section 1 above, provided that you also meet all of
+these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole or
+ in part contains or is derived from the Program or any part thereof, to be
+ licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+ c) If the modified program normally reads commands interactively when run,
+ you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms
+of this License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on
+the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and
+2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all
+the source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code
+distributed need not include anything that is normally distributed (in either
+source or binary form) with the major components (compiler, kernel, and so on)
+of the operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not
+accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to
+copy, distribute or modify the Program subject to these terms and conditions.
+You may not impose any further restrictions on the recipients' exercise of the
+rights granted herein. You are not responsible for enforcing compliance by
+third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In
+such case, this License incorporates the limitation as if written in the body
+of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any later
+version", you have the option of following the terms and conditions either of
+that version or of any later version published by the Free Software Foundation.
+If the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
+PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+ with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
+ software, and you are welcome to redistribute it under certain conditions;
+ type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than 'show w' and 'show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here
+is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ 'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General Public
+License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL
+
+Certain source files distributed by Oracle America and/or its affiliates are
+subject to the following clarification and special exception to the GPL, but
+only where Oracle has expressly included in the particular source file's header
+the words "Oracle designates this particular file as subject to the "Classpath"
+exception as provided by Oracle in the LICENSE file that accompanied this code."
+
+ Linking this library statically or dynamically with other modules is making
+ a combined work based on this library. Thus, the terms and conditions of
+ the GNU General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent modules,
+ and to copy and distribute the resulting executable under terms of your
+ choice, provided that you also meet, for each linked independent module,
+ the terms and conditions of the license of that module. An independent
+ module is a module which is not derived from or based on this library. If
+ you modify this library, you may extend this exception to your version of
+ the library, but you are not obligated to do so. If you do not wish to do
+ so, delete this exception statement from your version.
diff --git a/deliveries/Javadoc/legal/jquery.md b/deliveries/Javadoc/legal/jquery.md
new file mode 100644
index 00000000..d468b318
--- /dev/null
+++ b/deliveries/Javadoc/legal/jquery.md
@@ -0,0 +1,72 @@
+## jQuery v3.6.1
+
+### jQuery License
+```
+jQuery v 3.6.1
+Copyright OpenJS Foundation and other contributors, https://openjsf.org/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************
+
+The jQuery JavaScript Library v3.6.1 also includes Sizzle.js
+
+Sizzle.js includes the following license:
+
+Copyright JS Foundation and other contributors, https://js.foundation/
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/jquery/sizzle
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+All files located in the node_modules and external directories are
+externally maintained libraries used by this software which have their
+own licenses; we recommend you read them, as their terms may differ from
+the terms above.
+
+*********************
+
+```
diff --git a/deliveries/Javadoc/legal/jqueryUI.md b/deliveries/Javadoc/legal/jqueryUI.md
new file mode 100644
index 00000000..8bda9d7a
--- /dev/null
+++ b/deliveries/Javadoc/legal/jqueryUI.md
@@ -0,0 +1,49 @@
+## jQuery UI v1.13.2
+
+### jQuery UI License
+```
+Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/jquery/jquery-ui
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code contained within the demos directory.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+All files located in the node_modules and external directories are
+externally maintained libraries used by this software which have their
+own licenses; we recommend you read them, as their terms may differ from
+the terms above.
+
+```
diff --git a/deliveries/Javadoc/link.svg b/deliveries/Javadoc/link.svg
new file mode 100644
index 00000000..7ccc5ed0
--- /dev/null
+++ b/deliveries/Javadoc/link.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/deliveries/Javadoc/member-search-index.js b/deliveries/Javadoc/member-search-index.js
new file mode 100644
index 00000000..2010453b
--- /dev/null
+++ b/deliveries/Javadoc/member-search-index.js
@@ -0,0 +1 @@
+memberSearchIndex = [{"p":"it.polimi.ingsw.network.messages.actions","c":"ActionMessage","l":"ActionMessage(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"add(T)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"addCard(Pair, InitialCard, Side)","u":"addCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.InitialCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"addCard(Pair, PlayableCard, Side)","u":"addCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"ValidPositions","l":"addCard(ShownCard)","u":"addCard(it.polimi.ingsw.client.frontend.ShownCard)"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"addHandCard(PlayableCard)","u":"addHandCard(it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"addMatchCard(String, int, int, boolean)","u":"addMatchCard(java.lang.String,int,int,boolean)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"addPlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"WaitState","l":"addPlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"addPlayer(Player)","u":"addPlayer(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"addPlayer(String)","u":"addPlayer(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"addPlayer(String)","u":"addPlayer(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"addPlayerTab(String, Color)","u":"addPlayerTab(java.lang.String,it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"addPoints(int)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"addRanking(LeaderboardEntry)","u":"addRanking(it.polimi.ingsw.utils.LeaderboardEntry)"},{"p":"it.polimi.ingsw.gamemodel","c":"AfterDrawState","l":"AfterDrawState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.gamemodel","c":"AfterMoveState","l":"AfterMoveState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.exceptions","c":"AlreadyUsedUsernameException","l":"AlreadyUsedUsernameException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"ANIMAL"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"applyCSS(Parent, String)","u":"applyCSS(javafx.scene.Parent,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"InputHandler","l":"askUser()"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"AvailableMatch(String, Integer, Integer, boolean)","u":"%3Cinit%3E(java.lang.String,java.lang.Integer,java.lang.Integer,boolean)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"availableMatches"},{"p":"it.polimi.ingsw.network.messages.responses","c":"AvailableMatchesMessage","l":"AvailableMatchesMessage(Map)","u":"%3Cinit%3E(java.util.Map)"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"back"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"BACK"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"BLUE"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"Board()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"BoardPane()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"BoardPosition(boolean, Optional)","u":"%3Cinit%3E(boolean,java.util.Optional)"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"BOTTOM_LEFT"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"BOTTOM_RIGHT"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"bottomLeftCorner"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"bottomRightCorner"},{"p":"it.polimi.ingsw.gamemodel","c":"GoldCard","l":"calculatePoints(Board, Pair)","u":"calculatePoints(it.polimi.ingsw.gamemodel.Board,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserverCallable","l":"call(MatchObserver)","u":"call(it.polimi.ingsw.gamemodel.MatchObserver)"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"card()"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"Card()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"cardBorderH"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"cardBorderH"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"cardBorderW"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"cardBorderW"},{"p":"it.polimi.ingsw.exceptions","c":"CardException","l":"CardException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"CardFace","l":"CardFace(Symbol, Symbol, Symbol, Symbol, Set)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Symbol,it.polimi.ingsw.gamemodel.Symbol,it.polimi.ingsw.gamemodel.Symbol,it.polimi.ingsw.gamemodel.Symbol,java.util.Set)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"cardHeight"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"cardHeight"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"cardID()"},{"p":"it.polimi.ingsw.utils","c":"CardJsonParser","l":"CardJsonParser()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.utils","c":"CardsSerializer","l":"CardsSerializer()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"CardView()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"CardView(InitialCard, Side)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.InitialCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"CardView(Objective, Side)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Objective,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"CardView(PlayableCard, Side)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"cardWidth"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"cardWidth"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"changePlayer()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"changePlayer()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"changePlayer()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"chatContainer"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"chatHistoryScrollPane"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"chatPane"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"ChatPaneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"chooseInitialCardSide(Side)","u":"chooseInitialCardSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ChooseInitialCardSideMessage","l":"ChooseInitialCardSideMessage(String, Side)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseInitialSideState","l":"chooseInitialSide()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"chooseInitialSide()"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseInitialSideState","l":"ChooseInitialSideState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseSecretObjectiveState","l":"chooseSecretObjective()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"chooseSecretObjective()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"chooseSecretObjective(Objective)","u":"chooseSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ChooseSecretObjectiveMessage","l":"ChooseSecretObjectiveMessage(String, Integer)","u":"%3Cinit%3E(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseSecretObjectiveState","l":"ChooseSecretObjectiveState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.exceptions","c":"ChosenMatchException","l":"ChosenMatchException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"clearTerminal()"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"ClientBoard(Color, List)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Color,java.util.List)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"clientBoards"},{"p":"it.polimi.ingsw.network.tcp","c":"ClientListener","l":"ClientListener(Socket, Server)","u":"%3Cinit%3E(java.net.Socket,it.polimi.ingsw.server.Server)"},{"p":"it.polimi.ingsw.network.tcp","c":"ClientReceiver","l":"ClientReceiver(NetworkHandlerTCP, Socket)","u":"%3Cinit%3E(it.polimi.ingsw.client.network.NetworkHandlerTCP,java.net.Socket)"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"close()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"confirmSubmitBroadcastMessage(String)","u":"confirmSubmitBroadcastMessage(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"confirmSubmitPrivateMessage(String)","u":"confirmSubmitPrivateMessage(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"connected"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ConnectionSceneController","l":"ConnectionSceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"convertCoordinates(Pair)","u":"convertCoordinates(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"coords()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"CORNER_OBJ"},{"p":"it.polimi.ingsw.utils","c":"DeckCreator","l":"createGoldDeck()"},{"p":"it.polimi.ingsw.utils","c":"DeckCreator","l":"createInitialDeck()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"createMatch(String, int)","u":"createMatch(java.lang.String,int)"},{"p":"it.polimi.ingsw.server","c":"ServerRMIInterface","l":"createMatch(String, int)","u":"createMatch(java.lang.String,int)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"createMatch(String, Integer)","u":"createMatch(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"createMatch(String, Integer)","u":"createMatch(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"createMatch(String, Integer)","u":"createMatch(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"createMatch(String, Integer)","u":"createMatch(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"createMatch(String, Integer)","u":"createMatch(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"CreateMatchMessage","l":"CreateMatchMessage(String, String, int)","u":"%3Cinit%3E(java.lang.String,java.lang.String,int)"},{"p":"it.polimi.ingsw.utils","c":"DeckCreator","l":"createObjectiveDeck()"},{"p":"it.polimi.ingsw.utils","c":"DeckCreator","l":"createResourceDeck()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"createUsername"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"currentPlayer"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"currentPlayers()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"decideWinner()"},{"p":"it.polimi.ingsw.utils","c":"DeckCreator","l":"DeckCreator()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.exceptions","c":"DeckException","l":"DeckException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"decksTopReign"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"PlayerControls","l":"disable()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"disconnect()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"disconnect()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"disconnect()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"disconnect()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"doInitialTurnFinish()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"doStart()"},{"p":"it.polimi.ingsw.gamemodel","c":"AfterMoveState","l":"drawCard()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"drawCard()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"drawCard(DrawSource)","u":"drawCard(it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"drawCard(PlayableCard)","u":"drawCard(it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"DrawCardMessage","l":"DrawCardMessage(String, DrawSource)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.gamemodel","c":"NextTurnState","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"drawInitialCard()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"DrawInitialCardMessage","l":"DrawInitialCardMessage(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"drawSecretObjectives()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"DrawSecretObjectivesMessage","l":"DrawSecretObjectivesMessage(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"EMPTY_CORNER"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"PlayerControls","l":"enable()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"enableDrawSourcesInteractions(boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"enablePlaceCardInteractions(boolean)"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.gamemodel","c":"Objective","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"equals(Object)","u":"equals(java.lang.Object)"},{"p":"it.polimi.ingsw.network.messages.errors","c":"ErrorMessage","l":"ErrorMessage(String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"ErrorSceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"errorTitle"},{"p":"it.polimi.ingsw.utils","c":"RequestStatus","l":"FAILED"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"FEATHER"},{"p":"it.polimi.ingsw.gamemodel","c":"FinalState","l":"FinalState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"FIRST_VISIBLE"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"first()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"firstObjective"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"firstVisible"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"FOURTH_VISIBLE"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"fourthVisible"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"fromString(String)","u":"fromString(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"front"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"FRONT"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"FULL_CORNER"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"FUNGUS"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"GameDeck()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ActionMessage","l":"getAction()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"getAvailableMatches()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"getAvailableMatches()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"getAvailableMatches()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"getAvailableMatches()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"GetAvailableMatchesMessage","l":"GetAvailableMatchesMessage(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getAvailableResources()"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"getAvailableResources()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getAvailableResources()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getAvailableResources()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSetInitialSideMessage","l":"getAvailableResources()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"getBasicResources()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getBlackPawnImagePath()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"getBoard()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"getBoard()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacedCard","l":"getCard()"},{"p":"it.polimi.ingsw.utils","c":"CardJsonParser","l":"getCardBuilder()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"PlayCardMessage","l":"getCardID()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewCardMessage","l":"getCardID()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getCardID()"},{"p":"it.polimi.ingsw.gamemodel","c":"CardFace","l":"getCenter()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"getChatPane()"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getColor()"},{"p":"it.polimi.ingsw.gamemodel","c":"CardFace","l":"getCorner(Corner)","u":"getCorner(it.polimi.ingsw.gamemodel.Corner)"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"getCurrentHand()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getCurrentPlayer()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getCurrentPlayer()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"getCurrentPlayers()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getCurrentState()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getDecksTopReigns()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getDecksTopReigns()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewCardMessage","l":"getDeckTopReigns()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewCardMessage","l":"getDrawSource()"},{"p":"it.polimi.ingsw.network.messages.errors","c":"ErrorMessage","l":"getError()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getExceptionTitle(Exception)","u":"getExceptionTitle(java.lang.Exception)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewSecretObjectivesMessage","l":"getFirstID()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getFromFXML(String)","u":"getFromFXML(java.lang.String)"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"getGenericBack(Symbol, Pair)","u":"getGenericBack(it.polimi.ingsw.gamemodel.Symbol,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getGoldCards()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getGoldsBack(Symbol)","u":"getGoldsBack(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getHand()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getHexFromColor(Color)","u":"getHexFromColor(it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"getId()"},{"p":"it.polimi.ingsw.gamemodel","c":"Objective","l":"getID()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getImagePath(InitialCard, Side)","u":"getImagePath(it.polimi.ingsw.gamemodel.InitialCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getImagePath(Objective, Side)","u":"getImagePath(it.polimi.ingsw.gamemodel.Objective,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getImagePath(PlayableCard, Side)","u":"getImagePath(it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getInitialCard()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewInitialCardMessage","l":"getInitialCardID()"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getInitialCards()"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getInstance()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"getIO()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"getJoinableMatches()"},{"p":"it.polimi.ingsw.server","c":"ServerRMIInterface","l":"getJoinableMatches()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"getJoinableMatchesMap()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneJoinedMessage","l":"getJoinedPlayers()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneQuitMessage","l":"getJoinedPlayers()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getLoader(String)","u":"getLoader(java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"Server","l":"getMatch(String)","u":"getMatch(java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"AvailableMatchesMessage","l":"getMatches()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"CreateMatchMessage","l":"getMatchName()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"JoinMatchMessage","l":"getMatchName()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getMaxPlayers()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"CreateMatchMessage","l":"getMaxPlayers()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneJoinedMessage","l":"getMaxPlayers()"},{"p":"it.polimi.ingsw.network.messages.errors","c":"ErrorMessage","l":"getMessage()"},{"p":"it.polimi.ingsw.utils","c":"MessageJsonParser","l":"getMessageBuilder()"},{"p":"it.polimi.ingsw.gamemodel","c":"GoldCard","l":"getMultiplier()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"InputHandler","l":"getNextLine()"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getObjective()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ChooseSecretObjectiveMessage","l":"getObjectiveID()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneChoseSecretObjectiveMessage","l":"getObjectiveID()"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"getPawnColor()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getPawnImagePath(Color)","u":"getPawnImagePath(it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getPlaced()"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"getPlacedCards()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getPlacedCards()"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getPlacementNumber()"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getPlayableCards()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacedCard","l":"getPlayedCardFace()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacedCard","l":"getPlayedSide()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"getPlayer()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"getPlayerHands()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"getPlayerPawnColors()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getPlayerPoints()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getPlayers()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getPlayersFinalRanking()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getPlayersHands()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getPlayersUsernamesAndPawns()"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"getPoints()"},{"p":"it.polimi.ingsw.gamemodel","c":"GoldCard","l":"getPoints()"},{"p":"it.polimi.ingsw.gamemodel","c":"Objective","l":"getPoints()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"getPoints()"},{"p":"it.polimi.ingsw.gamemodel","c":"ResourceCard","l":"getPoints()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getPoints()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchFinishedMessage","l":"getRanking()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"SendPrivateTextMessage","l":"getRecipient()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSentPrivateTextMessage","l":"getRecipient()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlayableCard","l":"getReign()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"getReigns()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewCardMessage","l":"getReplacementCardID()"},{"p":"it.polimi.ingsw.gamemodel","c":"Objective","l":"getReq()"},{"p":"it.polimi.ingsw.gamemodel","c":"PositionRequirement","l":"getReqs()"},{"p":"it.polimi.ingsw.gamemodel","c":"QuantityRequirement","l":"getReqs()"},{"p":"it.polimi.ingsw.gamemodel","c":"GoldCard","l":"getRequirement()"},{"p":"it.polimi.ingsw.utils","c":"CardsManager","l":"getResourceCards()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"getResourcesBack(Symbol)","u":"getResourcesBack(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"ResponseMessage","l":"getResponse()"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"getRightColor(Symbol)","u":"getRightColor(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"getRightIcon(Symbol)","u":"getRightIcon(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewSecretObjectivesMessage","l":"getSecondID()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"getSecretObjective()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getSecretObjective()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ChooseInitialCardSideMessage","l":"getSide()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"PlayCardMessage","l":"getSide()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getSide()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSetInitialSideMessage","l":"getSide()"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"getSide(Side)","u":"getSide(it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"getSize()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"DrawCardMessage","l":"getSource()"},{"p":"it.polimi.ingsw.client.frontend","c":"LastRequest","l":"getStatus()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"SendBroadcastTextMessage","l":"getText()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"SendPrivateTextMessage","l":"getText()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSentBroadcastTextMessage","l":"getText()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSentPrivateTextMessage","l":"getText()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacedCard","l":"getTurn()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"getUsername()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"getUsername()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"getUsername()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"ActionMessage","l":"getUsername()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"ResponseMessage","l":"getUsername()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"getValidCorner()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"getValidMultiplier()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"ValidPositions","l":"getValidPlaces()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"getView()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"getVisibleCards()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"getVisibleDeckReigns()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getVisibleObjectives()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getVisibleObjectives()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"getVisibleObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"getVisiblePlayableCards()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"getVisiblePlayableCards()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"PlayCardMessage","l":"getX()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getX()"},{"p":"it.polimi.ingsw.network.messages.actions","c":"PlayCardMessage","l":"getY()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"getY()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"giveInitialCard(InitialCard)","u":"giveInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"giveSecretObjectives(Pair)","u":"giveSecretObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.gamemodel","c":"GoldCard","l":"GoldCard(CardFace, Symbol, Symbol, int, QuantityRequirement)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.CardFace,it.polimi.ingsw.gamemodel.Symbol,it.polimi.ingsw.gamemodel.Symbol,int,it.polimi.ingsw.gamemodel.QuantityRequirement)"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"GOLDS_DECK"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"goldsDeck"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalApplication","l":"GraphicalApplication()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"graphicalView"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"GraphicalView()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"GraphicalViewGUI(Stage)","u":"%3Cinit%3E(javafx.stage.Stage)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"GraphicalViewTUI()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"GREEN"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"GuiUtil()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"handCards"},{"p":"it.polimi.ingsw.exceptions","c":"HandException","l":"HandException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"hashCode()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"hashCode()"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"hashCode()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"hashCode()"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"hashCode()"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"hashCode()"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"hashCode()"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"hashCode()"},{"p":"it.polimi.ingsw.gamemodel","c":"Card","l":"id"},{"p":"it.polimi.ingsw.gamemodel","c":"InitialCard","l":"InitialCard(CardFace, CardFace)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.CardFace,it.polimi.ingsw.gamemodel.CardFace)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ConnectionSceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"initialize()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"initializePostController()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"initializePostController()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"initializePostController()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"initializePostController()"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"initialsPath"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"INKWELL"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"InputHandler","l":"InputHandler(TuiPrinter)","u":"%3Cinit%3E(it.polimi.ingsw.client.frontend.tui.TuiPrinter)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"INSECT"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacementOutcome","l":"INVALID_COORDS"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacementOutcome","l":"INVALID_ENOUGH_RESOURCES"},{"p":"it.polimi.ingsw.exceptions","c":"InvalidPlayerException","l":"InvalidPlayerException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"InvalidResourceException","l":"InvalidResourceException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"IOHandler(Socket)","u":"%3Cinit%3E(java.net.Socket)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"ipAddress"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"isConnected()"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"isConnected()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"isDrawPhase()"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"isEmpty()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"PlayerControls","l":"isEnabled()"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneQuitMessage","l":"isEndMatch()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"isFinished()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"isFull()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"isInitialTurnFinished()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"isLastTurn()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"isRejoinable()"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"isRejoinable()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"isStarted()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"isValid()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"ValidPositions","l":"isValid(Pair)","u":"isValid(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"isValidName(String)","u":"isValidName(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"joinMatch(String)","u":"joinMatch(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"joinMatch(String)","u":"joinMatch(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"joinMatch(String)","u":"joinMatch(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"joinMatch(String)","u":"joinMatch(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"joinMatch(String)","u":"joinMatch(java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"Server","l":"joinMatch(String, String)","u":"joinMatch(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"ServerRMIInterface","l":"joinMatch(String, String)","u":"joinMatch(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"JoinMatchMessage","l":"JoinMatchMessage(String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"joinTitle"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"lastRequest"},{"p":"it.polimi.ingsw.client.frontend","c":"LastRequest","l":"LastRequest()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"lastTurn"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"leaderboardContainer"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"LeaderboardEntry(String, Integer, boolean)","u":"%3Cinit%3E(java.lang.String,java.lang.Integer,boolean)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"link()"},{"p":"it.polimi.ingsw.network.tcp","c":"ClientListener","l":"listen()"},{"p":"it.polimi.ingsw.network.tcp","c":"TCPServer","l":"listen()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"loadScene(String)","u":"loadScene(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"MatchStatus","l":"LOBBY"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"LobbySceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalApplication","l":"main(String[])","u":"main(java.lang.String[])"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"main(String[])","u":"main(java.lang.String[])"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"main(String[])","u":"main(java.lang.String[])"},{"p":"it.polimi.ingsw.server","c":"Server","l":"main(String[])","u":"main(java.lang.String[])"},{"p":"it.polimi.ingsw.utils","c":"CardsSerializer","l":"main(String[])","u":"main(java.lang.String[])"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"makeMove()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"makeMove()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"makeMove()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"makeMove()"},{"p":"it.polimi.ingsw.gamemodel","c":"NextTurnState","l":"makeMove()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"makeMove(Pair, PlayableCard, Side)","u":"makeMove(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"match"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"match"},{"p":"it.polimi.ingsw.client.frontend","c":"MatchStatus","l":"MATCH_STATE"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"Match(int, GameDeck, GameDeck, GameDeck, GameDeck)","u":"%3Cinit%3E(int,it.polimi.ingsw.gamemodel.GameDeck,it.polimi.ingsw.gamemodel.GameDeck,it.polimi.ingsw.gamemodel.GameDeck,it.polimi.ingsw.gamemodel.GameDeck)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"matchFinished()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"matchFinished()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"matchFinished()"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"matchFinished()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"matchFinished(List)","u":"matchFinished(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"matchFinished(List)","u":"matchFinished(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"matchFinished(List)","u":"matchFinished(java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"matchFinished(List)","u":"matchFinished(java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"matchFinished(List)","u":"matchFinished(java.util.List)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchFinishedMessage","l":"MatchFinishedMessage(List>)","u":"%3Cinit%3E(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"matchName"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"matchName"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"matchNumberContainer"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"matchResumed()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"matchResumed()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"matchResumed()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"matchResumed(Map, Map>, Pair, Map, Pair, Objective, Map>, Map, PlacedCard>>, Map, String, boolean)","u":"matchResumed(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.Objective,java.util.Map,java.util.Map,java.util.Map,java.lang.String,boolean)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"matchResumed(Map, Map>, Pair, Map, Pair, Objective, Map>, Map, PlacedCard>>, Map, String, boolean)","u":"matchResumed(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.Objective,java.util.Map,java.util.Map,java.util.Map,java.lang.String,boolean)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchResumedMessage","l":"MatchResumedMessage(Map, Map>, Pair, Map, Pair, Integer, Map>, Map>, Map, String, boolean)","u":"%3Cinit%3E(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair,java.lang.Integer,java.util.Map,java.util.Map,java.util.Map,java.lang.String,boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"MatchSceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"matchStarted()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"matchStarted()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"matchStarted()"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"matchStarted()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"matchStarted(Map, Map>, Pair, Map, Pair)","u":"matchStarted(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"matchStarted(Map, Map>, Pair, Map, Pair)","u":"matchStarted(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"matchStarted(Map, Map>, Pair, Map, Pair)","u":"matchStarted(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"MatchStartedMessage","l":"MatchStartedMessage(Pair, Map, Pair, List)","u":"%3Cinit%3E(it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.List)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"MatchState()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"MatchState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"MatchStatusObserver(String, Map)","u":"%3Cinit%3E(java.lang.String,java.util.Map)"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"maxPlayers()"},{"p":"it.polimi.ingsw.network.messages","c":"Message","l":"Message()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.utils","c":"MessageJsonParser","l":"MessageJsonParser()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"msgToString(Message)","u":"msgToString(it.polimi.ingsw.network.messages.Message)"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"name()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"networkHandler"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"NetworkHandler(GraphicalView, String, int)","u":"%3Cinit%3E(it.polimi.ingsw.client.frontend.GraphicalView,java.lang.String,int)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"NetworkHandlerRMI(GraphicalView, String, int)","u":"%3Cinit%3E(it.polimi.ingsw.client.frontend.GraphicalView,java.lang.String,int)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"NetworkHandlerTCP(GraphicalView, String, Integer)","u":"%3Cinit%3E(it.polimi.ingsw.client.frontend.GraphicalView,java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"nextPlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"NextTurnState","l":"NextTurnState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"NO_MULT"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"noCardPath"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"notifyConnectionLost()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyConnectionLost()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"notifyConnectionLost()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"notifyError(Exception)","u":"notifyError(java.lang.Exception)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyError(Exception)","u":"notifyError(java.lang.Exception)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"notifyError(Exception)","u":"notifyError(java.lang.Exception)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"notifyError(Exception)","u":"notifyError(java.lang.Exception)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyError(String, String)","u":"notifyError(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"notifyLastTurn()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyLastTurn()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"notifyMatchResumed(boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyMatchResumed(boolean)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"notifyMatchResumed(boolean)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"notifyMatchStart()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"notifyMatchStarted()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"notifyMatchStarted()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"notifyMatchStarted()"},{"p":"it.polimi.ingsw.gamemodel","c":"Objective","l":"Objective(int, Requirement)","u":"%3Cinit%3E(int,it.polimi.ingsw.gamemodel.Requirement)"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"objectivesPath"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"Pair(T, U)","u":"%3Cinit%3E(T,U)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"PARCHMENT"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"parseCard(Card, Pair, Pair, Boolean)","u":"parseCard(it.polimi.ingsw.gamemodel.Card,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.utils.Pair,java.lang.Boolean)"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"parseObjective(Objective, Pair)","u":"parseObjective(it.polimi.ingsw.gamemodel.Objective,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"PlateauPane","l":"pawnSize"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"pawnsPath"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"peek()"},{"p":"it.polimi.ingsw.utils","c":"RequestStatus","l":"PENDING"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"ping()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"ping()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"ping()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"ping()"},{"p":"it.polimi.ingsw.server","c":"ServerRMIInterface","l":"ping()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"placeCard(Pair, PlayableCard, Side)","u":"placeCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"placeCard(Pair, PlayableCard, Side, int)","u":"placeCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"placeCard(Pair, PlayableCard, Side, Integer, Map)","u":"placeCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,java.lang.Integer,java.util.Map)"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacedCard","l":"PlacedCard(Card, Side, int)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Card,it.polimi.ingsw.gamemodel.Side,int)"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"PlacedCardRecord(Integer, Integer, Integer, Side)","u":"%3Cinit%3E(java.lang.Integer,java.lang.Integer,java.lang.Integer,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"placeInitial(Side, Map)","u":"placeInitial(it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"PLANT"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"plateauPane"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"PlateauPane","l":"PlateauPane()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlayableCard","l":"PlayableCard()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlayableCard","l":"PlayableCard(Symbol)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.utils","c":"GuiUtil","l":"playableCardsPath"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"playAgainButton"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"playCard(Pair, PlayableCard, Side)","u":"playCard(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"PlayCardMessage","l":"PlayCardMessage(String, Pair, Integer, Side)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.utils.Pair,java.lang.Integer,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"player"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"Player(Player)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"Player(String, Match)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"PlayerController(String, Match)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"PlayerControllerRMI(String, Match)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"PlayerControllerTCP(String, Match, IOHandler)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Match,it.polimi.ingsw.network.tcp.IOHandler)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"PlayerControls","l":"PlayerControls()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"players"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"playersContainer"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"PlayerTabController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlayableCard","l":"points"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"points()"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"poll()"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"pop()"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"port"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"PlateauPane","l":"positionOffset"},{"p":"it.polimi.ingsw.gamemodel","c":"PositionRequirement","l":"PositionRequirement(Map, Symbol>)","u":"%3Cinit%3E(java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printAvailableResources(Map, Integer)","u":"printAvailableResources(java.util.Map,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printCard(ShownCard)","u":"printCard(it.polimi.ingsw.client.frontend.ShownCard)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printCenteredMessage(String, int)","u":"printCenteredMessage(java.lang.String,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printChat(List)","u":"printChat(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printDrawingScreen(Pair, Map)","u":"printDrawingScreen(it.polimi.ingsw.utils.Pair,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printEndScreen(List, String)","u":"printEndScreen(java.util.List,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printHand(String, Color, List)","u":"printHand(java.lang.String,it.polimi.ingsw.gamemodel.Color,java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printHandAtBottom(List)","u":"printHandAtBottom(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printInitialSideBySide(InitialCard, int)","u":"printInitialSideBySide(it.polimi.ingsw.gamemodel.InitialCard,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printListReverse(List)","u":"printListReverse(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printMatchesLobby(List, List, int)","u":"printMatchesLobby(java.util.List,java.util.List,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printMessage(String)","u":"printMessage(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printMessages(List)","u":"printMessages(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printObjectivePair(String, Pair, int)","u":"printObjectivePair(java.lang.String,it.polimi.ingsw.utils.Pair,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printObjectives(String, Color, Objective, Pair)","u":"printObjectives(java.lang.String,it.polimi.ingsw.gamemodel.Color,it.polimi.ingsw.gamemodel.Objective,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printPlayableFrontAndBack(PlayableCard, int)","u":"printPlayableFrontAndBack(it.polimi.ingsw.gamemodel.PlayableCard,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printPlayerBoard(String, ClientBoard)","u":"printPlayerBoard(java.lang.String,it.polimi.ingsw.client.frontend.ClientBoard)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printPrompt(String)","u":"printPrompt(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printScoreboard(Map, int)","u":"printScoreboard(java.util.Map,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printSimpleList(List, Boolean, Boolean)","u":"printSimpleList(java.util.List,java.lang.Boolean,java.lang.Boolean)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printStringsBoxed(List, String, Color, Boolean)","u":"printStringsBoxed(java.util.List,java.lang.String,it.polimi.ingsw.gamemodel.Color,java.lang.Boolean)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printValidPlaces(Map, Pair>)","u":"printValidPlaces(java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"printWelcomeScreen()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"proposeSecretObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"proposeSecretObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"NextTurnState","l":"proposeSecretObjectives()"},{"p":"it.polimi.ingsw.gamemodel","c":"QuantityRequirement","l":"QuantityRequirement(Map)","u":"%3Cinit%3E(java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"RankingSceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"readMsg()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"receiveAvailableMatches(List)","u":"receiveAvailableMatches(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"receiveAvailableMatches(List)","u":"receiveAvailableMatches(java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"receiveAvailableMatches(List)","u":"receiveAvailableMatches(java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"receiveAvailableMatches(List)","u":"receiveAvailableMatches(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"receiveBroadcastMessage(String, String)","u":"receiveBroadcastMessage(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ChatPaneController","l":"receivePrivateMessage(String, String)","u":"receivePrivateMessage(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"RED"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"registerView(RemoteViewInterface)","u":"registerView(it.polimi.ingsw.client.network.RemoteViewInterface)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"registerView(RemoteViewInterface)","u":"registerView(it.polimi.ingsw.client.network.RemoteViewInterface)"},{"p":"it.polimi.ingsw.gamemodel","c":"PlayableCard","l":"reign"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"removeHandCard(PlayableCard)","u":"removeHandCard(it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.gamemodel","c":"FinalState","l":"removePlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"removePlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"WaitState","l":"removePlayer()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"removePlayer(Player)","u":"removePlayer(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"removePlayer(String)","u":"removePlayer(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"removePlayerChoiceContainer()"},{"p":"it.polimi.ingsw.gamemodel","c":"Requirement","l":"Requirement()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"ResourceCard","l":"ResourceCard(CardFace, Symbol, int)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.CardFace,it.polimi.ingsw.gamemodel.Symbol,int)"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"RESOURCES_DECK"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"resourcesDeck"},{"p":"it.polimi.ingsw.network.messages.responses","c":"ResponseMessage","l":"ResponseMessage(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"resumeMatch(Map, Map>, Pair, Map, Pair, Objective, Map>, Map, PlacedCard>>, Map, String, boolean)","u":"resumeMatch(java.util.Map,java.util.Map,it.polimi.ingsw.utils.Pair,java.util.Map,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.Objective,java.util.Map,java.util.Map,java.util.Map,java.lang.String,boolean)"},{"p":"it.polimi.ingsw.network.tcp","c":"ClientListener","l":"run()"},{"p":"it.polimi.ingsw.network.tcp","c":"ClientReceiver","l":"run()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"SceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalApplication","l":"screenHeight"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalApplication","l":"screenWidth"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"SECOND_VISIBLE"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"second()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"secondObjective"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"secondVisible"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"sendBroadcastText(Player, String)","u":"sendBroadcastText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"sendBroadcastText(String)","u":"sendBroadcastText(java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"SendBroadcastTextMessage","l":"SendBroadcastTextMessage(String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerController","l":"sendJoined()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"sendPrivateText(Player, Player, String)","u":"sendPrivateText(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"sendPrivateText(Player, String)","u":"sendPrivateText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerRMI","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandlerTCP","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMIInterface","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"sendPrivateText(String, String)","u":"sendPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.actions","c":"SendPrivateTextMessage","l":"SendPrivateTextMessage(String, String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"Server","l":"Server(int, int)","u":"%3Cinit%3E(int,int)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ConnectionSceneController","l":"serverAddress"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ConnectionSceneController","l":"serverPort"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setArc(double)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setCard(InitialCard, Side)","u":"setCard(it.polimi.ingsw.gamemodel.InitialCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setCard(Objective, Side)","u":"setCard(it.polimi.ingsw.gamemodel.Objective,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setCard(PlayableCard, Side)","u":"setCard(it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"setColor(Color)","u":"setColor(it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"PlateauPane","l":"setColor(String, Color)","u":"setColor(java.lang.String,it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.gamemodel","c":"Player","l":"setConnected(boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"setControllerAttributes(FXMLLoader)","u":"setControllerAttributes(javafx.fxml.FXMLLoader)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"setControllerAttributes(FXMLLoader, Node)","u":"setControllerAttributes(javafx.fxml.FXMLLoader,javafx.scene.Node)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setCurrentPlayer(boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"setCurrentPlayers(int)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setDrawSource(DrawSource, PlayableCard, Symbol)","u":"setDrawSource(it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"setErrror(Exception)","u":"setErrror(java.lang.Exception)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setFocus(String)","u":"setFocus(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setFocusToTable()"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setGoldsCardBack(Symbol)","u":"setGoldsCardBack(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"setGraphicalView(GraphicalViewGUI)","u":"setGraphicalView(it.polimi.ingsw.client.frontend.gui.GraphicalViewGUI)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setHandCards(List)","u":"setHandCards(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"setInitial(InitialCard)","u":"setInitial(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"setInitialCard(InitialCard, Side)","u":"setInitialCard(it.polimi.ingsw.gamemodel.InitialCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setInitialSide(Side, Map)","u":"setInitialSide(it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"setLastRequestStatus(RequestStatus)","u":"setLastRequestStatus(it.polimi.ingsw.utils.RequestStatus)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"setLastRequestStatus(RequestStatus)","u":"setLastRequestStatus(it.polimi.ingsw.utils.RequestStatus)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"setLobbySceneController(LobbySceneController)","u":"setLobbySceneController(it.polimi.ingsw.client.frontend.gui.controllers.LobbySceneController)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"setMatchName(String)","u":"setMatchName(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"setMaxPlayers(int)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"setNetworkHandler(NetworkHandler)","u":"setNetworkHandler(it.polimi.ingsw.client.network.NetworkHandler)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setObjectives(Pair)","u":"setObjectives(it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setPlateauPoints(String, int)","u":"setPlateauPoints(java.lang.String,int)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setPoints(int)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"PlateauPane","l":"setPoints(String, int)","u":"setPoints(java.lang.String,int)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"InputHandler","l":"setPrompt(String)","u":"setPrompt(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setResources(Map)","u":"setResources(java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setResourcesCardBack(Symbol)","u":"setResourcesCardBack(it.polimi.ingsw.gamemodel.Symbol)"},{"p":"it.polimi.ingsw.client.frontend","c":"ClientBoard","l":"setSecretObjective(Objective)","u":"setSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setSecretObjective(Objective)","u":"setSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setSecretObjective(Objective)","u":"setSecretObjective(it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"setStage(Stage)","u":"setStage(javafx.stage.Stage)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setState(MatchState)","u":"setState(it.polimi.ingsw.gamemodel.MatchState)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"setStateTitle(String)","u":"setStateTitle(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setStateTitle(String)","u":"setStateTitle(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"LastRequest","l":"setStatus(RequestStatus)","u":"setStatus(it.polimi.ingsw.utils.RequestStatus)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"setText(String)","u":"setText(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"setTitle(String)","u":"setTitle(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"setToken(Color)","u":"setToken(it.polimi.ingsw.gamemodel.Color)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setupBoards()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setupDecks()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"setupPlayers()"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"setUsername(String)","u":"setUsername(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"setUsername(String)","u":"setUsername(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"setUsername(String)","u":"setUsername(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"setUsername(String)","u":"setUsername(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"setVictory(boolean)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"showConnectionScene()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"showMatch()"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"ShownCard(Card, Side, Pair)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Card,it.polimi.ingsw.gamemodel.Side,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"InputHandler","l":"showPrompt()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"showRankingScene()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"showWaitScene()"},{"p":"it.polimi.ingsw.gamemodel","c":"GameDeck","l":"shuffle()"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"side()"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"side()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneChoseSecretObjective(Player, Objective)","u":"someoneChoseSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneChoseSecretObjective(Player, Objective)","u":"someoneChoseSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneChoseSecretObjective(Player, Objective)","u":"someoneChoseSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneChoseSecretObjective(Player, Objective)","u":"someoneChoseSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Objective)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneChoseSecretObjective(String)","u":"someoneChoseSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneChoseSecretObjective(String)","u":"someoneChoseSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someoneChoseSecretObjective(String)","u":"someoneChoseSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneChoseSecretObjective(String)","u":"someoneChoseSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneChoseSecretObjective(String)","u":"someoneChoseSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneChoseSecretObjectiveMessage","l":"SomeoneChoseSecretObjectiveMessage(String, Integer)","u":"%3Cinit%3E(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneDrewCard(Player, DrawSource, PlayableCard, PlayableCard)","u":"someoneDrewCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneDrewCard(Player, DrawSource, PlayableCard, PlayableCard)","u":"someoneDrewCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneDrewCard(Player, DrawSource, PlayableCard, PlayableCard)","u":"someoneDrewCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneDrewCard(Player, DrawSource, PlayableCard, PlayableCard)","u":"someoneDrewCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneDrewCard(String, DrawSource, PlayableCard, PlayableCard, Pair)","u":"someoneDrewCard(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneDrewCard(String, DrawSource, PlayableCard, PlayableCard, Pair)","u":"someoneDrewCard(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneDrewCard(String, DrawSource, PlayableCard, PlayableCard, Pair)","u":"someoneDrewCard(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneDrewCard(String, DrawSource, PlayableCard, PlayableCard, Pair)","u":"someoneDrewCard(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewCardMessage","l":"SomeoneDrewCardMessage(String, DrawSource, Integer, Integer, Pair)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.DrawSource,java.lang.Integer,java.lang.Integer,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"someoneDrewInitialCard(InitialCard)","u":"someoneDrewInitialCard(it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneDrewInitialCard(Player, InitialCard)","u":"someoneDrewInitialCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneDrewInitialCard(Player, InitialCard)","u":"someoneDrewInitialCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneDrewInitialCard(Player, InitialCard)","u":"someoneDrewInitialCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneDrewInitialCard(Player, InitialCard)","u":"someoneDrewInitialCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneDrewInitialCard(String, InitialCard)","u":"someoneDrewInitialCard(java.lang.String,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneDrewInitialCard(String, InitialCard)","u":"someoneDrewInitialCard(java.lang.String,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneDrewInitialCard(String, InitialCard)","u":"someoneDrewInitialCard(java.lang.String,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneDrewInitialCard(String, InitialCard)","u":"someoneDrewInitialCard(java.lang.String,it.polimi.ingsw.gamemodel.InitialCard)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewInitialCardMessage","l":"SomeoneDrewInitialCardMessage(String, Integer)","u":"%3Cinit%3E(java.lang.String,java.lang.Integer)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"PlayerTabController","l":"someoneDrewSecretObjective()"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneDrewSecretObjective(Player, Pair)","u":"someoneDrewSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneDrewSecretObjective(Player, Pair)","u":"someoneDrewSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneDrewSecretObjective(Player, Pair)","u":"someoneDrewSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneDrewSecretObjective(Player, Pair)","u":"someoneDrewSecretObjective(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneDrewSecretObjective(String)","u":"someoneDrewSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneDrewSecretObjective(String)","u":"someoneDrewSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneDrewSecretObjective(String)","u":"someoneDrewSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneDrewSecretObjective(String)","u":"someoneDrewSecretObjective(java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneDrewSecretObjectivesMessage","l":"SomeoneDrewSecretObjectivesMessage(String, Pair)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.utils.Pair)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneJoined(Player)","u":"someoneJoined(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneJoined(Player)","u":"someoneJoined(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneJoined(Player)","u":"someoneJoined(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneJoined(Player)","u":"someoneJoined(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneJoined(String, List)","u":"someoneJoined(java.lang.String,java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneJoined(String, List)","u":"someoneJoined(java.lang.String,java.util.List)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someoneJoined(String, List)","u":"someoneJoined(java.lang.String,java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneJoined(String, List)","u":"someoneJoined(java.lang.String,java.util.List)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneJoined(String, List)","u":"someoneJoined(java.lang.String,java.util.List)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneJoinedMessage","l":"SomeoneJoinedMessage(String, List, int)","u":"%3Cinit%3E(java.lang.String,java.util.List,int)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someonePlayedCard(Player, Pair, PlayableCard, Side)","u":"someonePlayedCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someonePlayedCard(Player, Pair, PlayableCard, Side)","u":"someonePlayedCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someonePlayedCard(Player, Pair, PlayableCard, Side)","u":"someonePlayedCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someonePlayedCard(Player, Pair, PlayableCard, Side)","u":"someonePlayedCard(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someonePlayedCard(String, Pair, PlayableCard, Side, int, Map)","u":"someonePlayedCard(java.lang.String,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someonePlayedCard(String, Pair, PlayableCard, Side, int, Map)","u":"someonePlayedCard(java.lang.String,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someonePlayedCard(String, Pair, PlayableCard, Side, int, Map)","u":"someonePlayedCard(java.lang.String,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someonePlayedCard(String, Pair, PlayableCard, Side, int, Map)","u":"someonePlayedCard(java.lang.String,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someonePlayedCard(String, Pair, PlayableCard, Side, int, Map)","u":"someonePlayedCard(java.lang.String,it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.PlayableCard,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeonePlayedCardMessage","l":"SomeonePlayedCardMessage(String, Pair, Integer, Side, int, Map)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.utils.Pair,java.lang.Integer,it.polimi.ingsw.gamemodel.Side,int,java.util.Map)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneQuit(Player)","u":"someoneQuit(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneQuit(Player)","u":"someoneQuit(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneQuit(Player)","u":"someoneQuit(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneQuit(Player)","u":"someoneQuit(it.polimi.ingsw.gamemodel.Player)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneQuit(String)","u":"someoneQuit(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneQuit(String)","u":"someoneQuit(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someoneQuit(String)","u":"someoneQuit(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneQuit(String)","u":"someoneQuit(java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneQuit(String)","u":"someoneQuit(java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneQuitMessage","l":"SomeoneQuitMessage(String, int, boolean)","u":"%3Cinit%3E(java.lang.String,int,boolean)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneSentBroadcastText(Player, String)","u":"someoneSentBroadcastText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneSentBroadcastText(Player, String)","u":"someoneSentBroadcastText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneSentBroadcastText(Player, String)","u":"someoneSentBroadcastText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneSentBroadcastText(Player, String)","u":"someoneSentBroadcastText(it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneSentBroadcastText(String, String)","u":"someoneSentBroadcastText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneSentBroadcastText(String, String)","u":"someoneSentBroadcastText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someoneSentBroadcastText(String, String)","u":"someoneSentBroadcastText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneSentBroadcastText(String, String)","u":"someoneSentBroadcastText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneSentBroadcastText(String, String)","u":"someoneSentBroadcastText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSentBroadcastTextMessage","l":"SomeoneSentBroadcastTextMessage(String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneSentPrivateText(Player, Player, String)","u":"someoneSentPrivateText(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneSentPrivateText(Player, Player, String)","u":"someoneSentPrivateText(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneSentPrivateText(Player, Player, String)","u":"someoneSentPrivateText(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneSentPrivateText(Player, Player, String)","u":"someoneSentPrivateText(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Player,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneSentPrivateText(String, String)","u":"someoneSentPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneSentPrivateText(String, String)","u":"someoneSentPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"GraphicalViewTUI","l":"someoneSentPrivateText(String, String)","u":"someoneSentPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneSentPrivateText(String, String)","u":"someoneSentPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneSentPrivateText(String, String)","u":"someoneSentPrivateText(java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSentPrivateTextMessage","l":"SomeoneSentPrivateTextMessage(String, String, String)","u":"%3Cinit%3E(java.lang.String,java.lang.String,java.lang.String)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerRMI","l":"someoneSetInitialSide(Player, Side, Map)","u":"someoneSetInitialSide(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.controllers","c":"PlayerControllerTCP","l":"someoneSetInitialSide(Player, Side, Map)","u":"someoneSetInitialSide(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchObserver","l":"someoneSetInitialSide(Player, Side, Map)","u":"someoneSetInitialSide(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.server","c":"MatchStatusObserver","l":"someoneSetInitialSide(Player, Side, Map)","u":"someoneSetInitialSide(it.polimi.ingsw.gamemodel.Player,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"someoneSetInitialSide(String, Side, Map)","u":"someoneSetInitialSide(java.lang.String,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalViewGUI","l":"someoneSetInitialSide(String, Side, Map)","u":"someoneSetInitialSide(java.lang.String,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"someoneSetInitialSide(String, Side, Map)","u":"someoneSetInitialSide(java.lang.String,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.network","c":"RemoteViewInterface","l":"someoneSetInitialSide(String, Side, Map)","u":"someoneSetInitialSide(java.lang.String,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.network.messages.responses","c":"SomeoneSetInitialSideMessage","l":"SomeoneSetInitialSideMessage(String, Side, Map)","u":"%3Cinit%3E(java.lang.String,it.polimi.ingsw.gamemodel.Side,java.util.Map)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"stage"},{"p":"it.polimi.ingsw.client.frontend.gui","c":"GraphicalApplication","l":"start(Stage)","u":"start(javafx.stage.Stage)"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"startConnectionCheck()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"startRMIServer()"},{"p":"it.polimi.ingsw.server","c":"Server","l":"startTCPServer()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"stateTitle"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"stringToMsg(String)","u":"stringToMsg(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"subscribeObserver(MatchObserver)","u":"subscribeObserver(it.polimi.ingsw.gamemodel.MatchObserver)"},{"p":"it.polimi.ingsw.utils","c":"RequestStatus","l":"SUCCESSFUL"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"tableSize"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"tableTab"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"BoardPane","l":"takenSpots"},{"p":"it.polimi.ingsw.network.tcp","c":"TCPServer","l":"TCPServer(Integer, Server)","u":"%3Cinit%3E(java.lang.Integer,it.polimi.ingsw.server.Server)"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"THIRD_VISIBLE"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"MatchSceneController","l":"thirdVisible"},{"p":"it.polimi.ingsw.gamemodel","c":"PositionRequirement","l":"timesMet(Board)","u":"timesMet(it.polimi.ingsw.gamemodel.Board)"},{"p":"it.polimi.ingsw.gamemodel","c":"QuantityRequirement","l":"timesMet(Board)","u":"timesMet(it.polimi.ingsw.gamemodel.Board)"},{"p":"it.polimi.ingsw.gamemodel","c":"Requirement","l":"timesMet(Board)","u":"timesMet(it.polimi.ingsw.gamemodel.Board)"},{"p":"it.polimi.ingsw.utils","c":"MessageJsonParser","l":"toJson(Message)","u":"toJson(it.polimi.ingsw.network.messages.Message)"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"tokenRadius"},{"p":"it.polimi.ingsw.utils","c":"MessageJsonParser","l":"toMessage(String)","u":"toMessage(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"TOP_LEFT"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"TOP_RIGHT"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"topLeftCorner"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","c":"CardView","l":"topRightCorner"},{"p":"it.polimi.ingsw.client.frontend","c":"ShownCard","l":"toString()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"BoardPosition","l":"toString()"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"toString()"},{"p":"it.polimi.ingsw.utils","c":"AvailableMatch","l":"toString()"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"toString()"},{"p":"it.polimi.ingsw.utils","c":"Pair","l":"toString()"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"toString()"},{"p":"it.polimi.ingsw.gamemodel","c":"AfterDrawState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"AfterMoveState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseInitialSideState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"ChooseSecretObjectiveState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"FinalState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"MatchState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"NextTurnState","l":"transition()"},{"p":"it.polimi.ingsw.gamemodel","c":"WaitState","l":"transition()"},{"p":"it.polimi.ingsw.utils","c":"TUICardParser","l":"TUICardParser()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"TuiPrinter","l":"TuiPrinter()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"Match","l":"unsubscribeObserver(MatchObserver)","u":"unsubscribeObserver(it.polimi.ingsw.gamemodel.MatchObserver)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"updateLabel()"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"LobbySceneController","l":"updateMatches(List)","u":"updateMatches(java.util.List)"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"username"},{"p":"it.polimi.ingsw.client.network","c":"NetworkHandler","l":"username"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"username()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacementOutcome","l":"VALID"},{"p":"it.polimi.ingsw.client.frontend.tui","c":"ValidPositions","l":"ValidPositions()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.client.frontend","c":"MatchStatus","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacementOutcome","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.utils","c":"RequestStatus","l":"valueOf(String)","u":"valueOf(java.lang.String)"},{"p":"it.polimi.ingsw.client.frontend","c":"MatchStatus","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"Corner","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"DrawSource","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"PlacementOutcome","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"Side","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"Symbol","l":"values()"},{"p":"it.polimi.ingsw.utils","c":"RequestStatus","l":"values()"},{"p":"it.polimi.ingsw.gamemodel","c":"Board","l":"verifyCardPlacement(Pair, Card, Side)","u":"verifyCardPlacement(it.polimi.ingsw.utils.Pair,it.polimi.ingsw.gamemodel.Card,it.polimi.ingsw.gamemodel.Side)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"RankingSceneController","l":"victoryLabel"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"SceneController","l":"view"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"visibleObjectives"},{"p":"it.polimi.ingsw.client.frontend","c":"GraphicalView","l":"visiblePlayableCards"},{"p":"it.polimi.ingsw.client.frontend","c":"MatchStatus","l":"WAIT_STATE"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"WaitingSceneController","l":"WaitingSceneController()","u":"%3Cinit%3E()"},{"p":"it.polimi.ingsw.gamemodel","c":"WaitState","l":"WaitState(Match)","u":"%3Cinit%3E(it.polimi.ingsw.gamemodel.Match)"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"windowHeight"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","c":"ErrorSceneController","l":"windowWidth"},{"p":"it.polimi.ingsw.utils","c":"LeaderboardEntry","l":"winner()"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"writeMsg(Message)","u":"writeMsg(it.polimi.ingsw.network.messages.Message)"},{"p":"it.polimi.ingsw.network.tcp","c":"IOHandler","l":"writeMsg(String)","u":"writeMsg(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"WrongChoiceException","l":"WrongChoiceException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"WrongInputFormatException","l":"WrongInputFormatException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"WrongNameException","l":"WrongNameException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"WrongStateException","l":"WrongStateException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.exceptions","c":"WrongTurnException","l":"WrongTurnException(String)","u":"%3Cinit%3E(java.lang.String)"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"x()"},{"p":"it.polimi.ingsw.utils","c":"PlacedCardRecord","l":"y()"},{"p":"it.polimi.ingsw.gamemodel","c":"Color","l":"YELLOW"}];updateSearchResults();
\ No newline at end of file
diff --git a/deliveries/Javadoc/module-search-index.js b/deliveries/Javadoc/module-search-index.js
new file mode 100644
index 00000000..0d59754f
--- /dev/null
+++ b/deliveries/Javadoc/module-search-index.js
@@ -0,0 +1 @@
+moduleSearchIndex = [];updateSearchResults();
\ No newline at end of file
diff --git a/deliveries/Javadoc/overview-summary.html b/deliveries/Javadoc/overview-summary.html
new file mode 100644
index 00000000..0da0f90f
--- /dev/null
+++ b/deliveries/Javadoc/overview-summary.html
@@ -0,0 +1,26 @@
+
+
+
+
+AM08 1.0-SNAPSHOT API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The help page provides an introduction to the scope and syntax of JavaDoc search.
+
You can use the <ctrl> or <cmd> keys in combination with the left and right arrow keys to switch between result tabs in this page.
+
The URL template below may be used to configure this page as a search engine in browsers that support this feature. It has been tested to work in Google Chrome and Mozilla Firefox. Note that other browsers may not support this feature or require a different URL format.
+link
+
+
+
+
+
Loading search index...
+
+
+
+
+
+
+
+
+
+
diff --git a/deliveries/Javadoc/search.js b/deliveries/Javadoc/search.js
new file mode 100644
index 00000000..d3986705
--- /dev/null
+++ b/deliveries/Javadoc/search.js
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+"use strict";
+const messages = {
+ enterTerm: "Enter a search term",
+ noResult: "No results found",
+ oneResult: "Found one result",
+ manyResults: "Found {0} results",
+ loading: "Loading search index...",
+ searching: "Searching...",
+ redirecting: "Redirecting to first result...",
+ linkIcon: "Link icon",
+ linkToSection: "Link to this section"
+}
+const categories = {
+ modules: "Modules",
+ packages: "Packages",
+ types: "Classes and Interfaces",
+ members: "Members",
+ searchTags: "Search Tags"
+};
+const highlight = "$&";
+const NO_MATCH = {};
+const MAX_RESULTS = 300;
+function checkUnnamed(name, separator) {
+ return name === "" || !name ? "" : name + separator;
+}
+function escapeHtml(str) {
+ return str.replace(//g, ">");
+}
+function getHighlightedText(str, boundaries, from, to) {
+ var start = from;
+ var text = "";
+ for (var i = 0; i < boundaries.length; i += 2) {
+ var b0 = boundaries[i];
+ var b1 = boundaries[i + 1];
+ if (b0 >= to || b1 <= from) {
+ continue;
+ }
+ text += escapeHtml(str.slice(start, Math.max(start, b0)));
+ text += "";
+ text += escapeHtml(str.slice(Math.max(start, b0), Math.min(to, b1)));
+ text += "";
+ start = Math.min(to, b1);
+ }
+ text += escapeHtml(str.slice(start, to));
+ return text;
+}
+function getURLPrefix(item, category) {
+ var urlPrefix = "";
+ var slash = "/";
+ if (category === "modules") {
+ return item.l + slash;
+ } else if (category === "packages" && item.m) {
+ return item.m + slash;
+ } else if (category === "types" || category === "members") {
+ if (item.m) {
+ urlPrefix = item.m + slash;
+ } else {
+ $.each(packageSearchIndex, function(index, it) {
+ if (it.m && item.p === it.l) {
+ urlPrefix = it.m + slash;
+ }
+ });
+ }
+ }
+ return urlPrefix;
+}
+function getURL(item, category) {
+ if (item.url) {
+ return item.url;
+ }
+ var url = getURLPrefix(item, category);
+ if (category === "modules") {
+ url += "module-summary.html";
+ } else if (category === "packages") {
+ if (item.u) {
+ url = item.u;
+ } else {
+ url += item.l.replace(/\./g, '/') + "/package-summary.html";
+ }
+ } else if (category === "types") {
+ if (item.u) {
+ url = item.u;
+ } else {
+ url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.l + ".html";
+ }
+ } else if (category === "members") {
+ url += checkUnnamed(item.p, "/").replace(/\./g, '/') + item.c + ".html" + "#";
+ if (item.u) {
+ url += item.u;
+ } else {
+ url += item.l;
+ }
+ } else if (category === "searchTags") {
+ url += item.u;
+ }
+ item.url = url;
+ return url;
+}
+function createMatcher(term, camelCase) {
+ if (camelCase && !isUpperCase(term)) {
+ return null; // no need for camel-case matcher for lower case query
+ }
+ var pattern = "";
+ var upperCase = [];
+ term.trim().split(/\s+/).forEach(function(w, index, array) {
+ var tokens = w.split(/(?=[A-Z,.()<>?[\/])/);
+ for (var i = 0; i < tokens.length; i++) {
+ var s = tokens[i];
+ // ',' and '?' are the only delimiters commonly followed by space in java signatures
+ pattern += "(" + $.ui.autocomplete.escapeRegex(s).replace(/[,?]/g, "$&\\s*?") + ")";
+ upperCase.push(false);
+ var isWordToken = /\w$/.test(s);
+ if (isWordToken) {
+ if (i === tokens.length - 1 && index < array.length - 1) {
+ // space in query string matches all delimiters
+ pattern += "(.*?)";
+ upperCase.push(isUpperCase(s[0]));
+ } else {
+ if (!camelCase && isUpperCase(s) && s.length === 1) {
+ pattern += "()";
+ } else {
+ pattern += "([a-z0-9$<>?[\\]]*?)";
+ }
+ upperCase.push(isUpperCase(s[0]));
+ }
+ } else {
+ pattern += "()";
+ upperCase.push(false);
+ }
+ }
+ });
+ var re = new RegExp(pattern, "gi");
+ re.upperCase = upperCase;
+ return re;
+}
+function findMatch(matcher, input, startOfName, endOfName) {
+ var from = startOfName;
+ matcher.lastIndex = from;
+ var match = matcher.exec(input);
+ // Expand search area until we get a valid result or reach the beginning of the string
+ while (!match || match.index + match[0].length < startOfName || endOfName < match.index) {
+ if (from === 0) {
+ return NO_MATCH;
+ }
+ from = input.lastIndexOf(".", from - 2) + 1;
+ matcher.lastIndex = from;
+ match = matcher.exec(input);
+ }
+ var boundaries = [];
+ var matchEnd = match.index + match[0].length;
+ var score = 5;
+ var start = match.index;
+ var prevEnd = -1;
+ for (var i = 1; i < match.length; i += 2) {
+ var isUpper = isUpperCase(input[start]);
+ var isMatcherUpper = matcher.upperCase[i];
+ // capturing groups come in pairs, match and non-match
+ boundaries.push(start, start + match[i].length);
+ // make sure groups are anchored on a left word boundary
+ var prevChar = input[start - 1] || "";
+ var nextChar = input[start + 1] || "";
+ if (start !== 0 && !/[\W_]/.test(prevChar) && !/[\W_]/.test(input[start])) {
+ if (isUpper && (isLowerCase(prevChar) || isLowerCase(nextChar))) {
+ score -= 0.1;
+ } else if (isMatcherUpper && start === prevEnd) {
+ score -= isUpper ? 0.1 : 1.0;
+ } else {
+ return NO_MATCH;
+ }
+ }
+ prevEnd = start + match[i].length;
+ start += match[i].length + match[i + 1].length;
+
+ // lower score for parts of the name that are missing
+ if (match[i + 1] && prevEnd < endOfName) {
+ score -= rateNoise(match[i + 1]);
+ }
+ }
+ // lower score if a type name contains unmatched camel-case parts
+ if (input[matchEnd - 1] !== "." && endOfName > matchEnd)
+ score -= rateNoise(input.slice(matchEnd, endOfName));
+ score -= rateNoise(input.slice(0, Math.max(startOfName, match.index)));
+
+ if (score <= 0) {
+ return NO_MATCH;
+ }
+ return {
+ input: input,
+ score: score,
+ boundaries: boundaries
+ };
+}
+function isUpperCase(s) {
+ return s !== s.toLowerCase();
+}
+function isLowerCase(s) {
+ return s !== s.toUpperCase();
+}
+function rateNoise(str) {
+ return (str.match(/([.(])/g) || []).length / 5
+ + (str.match(/([A-Z]+)/g) || []).length / 10
+ + str.length / 20;
+}
+function doSearch(request, response) {
+ var term = request.term.trim();
+ var maxResults = request.maxResults || MAX_RESULTS;
+ if (term.length === 0) {
+ return this.close();
+ }
+ var matcher = {
+ plainMatcher: createMatcher(term, false),
+ camelCaseMatcher: createMatcher(term, true)
+ }
+ var indexLoaded = indexFilesLoaded();
+
+ function getPrefix(item, category) {
+ switch (category) {
+ case "packages":
+ return checkUnnamed(item.m, "/");
+ case "types":
+ return checkUnnamed(item.p, ".");
+ case "members":
+ return checkUnnamed(item.p, ".") + item.c + ".";
+ default:
+ return "";
+ }
+ }
+ function useQualifiedName(category) {
+ switch (category) {
+ case "packages":
+ return /[\s/]/.test(term);
+ case "types":
+ case "members":
+ return /[\s.]/.test(term);
+ default:
+ return false;
+ }
+ }
+ function searchIndex(indexArray, category) {
+ var matches = [];
+ if (!indexArray) {
+ if (!indexLoaded) {
+ matches.push({ l: messages.loading, category: category });
+ }
+ return matches;
+ }
+ $.each(indexArray, function (i, item) {
+ var prefix = getPrefix(item, category);
+ var simpleName = item.l;
+ var qualifiedName = prefix + simpleName;
+ var useQualified = useQualifiedName(category);
+ var input = useQualified ? qualifiedName : simpleName;
+ var startOfName = useQualified ? prefix.length : 0;
+ var endOfName = category === "members" && input.indexOf("(", startOfName) > -1
+ ? input.indexOf("(", startOfName) : input.length;
+ var m = findMatch(matcher.plainMatcher, input, startOfName, endOfName);
+ if (m === NO_MATCH && matcher.camelCaseMatcher) {
+ m = findMatch(matcher.camelCaseMatcher, input, startOfName, endOfName);
+ }
+ if (m !== NO_MATCH) {
+ m.indexItem = item;
+ m.prefix = prefix;
+ m.category = category;
+ if (!useQualified) {
+ m.input = qualifiedName;
+ m.boundaries = m.boundaries.map(function(b) {
+ return b + prefix.length;
+ });
+ }
+ matches.push(m);
+ }
+ return true;
+ });
+ return matches.sort(function(e1, e2) {
+ return e2.score - e1.score;
+ }).slice(0, maxResults);
+ }
+
+ var result = searchIndex(moduleSearchIndex, "modules")
+ .concat(searchIndex(packageSearchIndex, "packages"))
+ .concat(searchIndex(typeSearchIndex, "types"))
+ .concat(searchIndex(memberSearchIndex, "members"))
+ .concat(searchIndex(tagSearchIndex, "searchTags"));
+
+ if (!indexLoaded) {
+ updateSearchResults = function() {
+ doSearch(request, response);
+ }
+ } else {
+ updateSearchResults = function() {};
+ }
+ response(result);
+}
+// JQuery search menu implementation
+$.widget("custom.catcomplete", $.ui.autocomplete, {
+ _create: function() {
+ this._super();
+ this.widget().menu("option", "items", "> .result-item");
+ // workaround for search result scrolling
+ this.menu._scrollIntoView = function _scrollIntoView( item ) {
+ var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
+ if ( this._hasScroll() ) {
+ borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
+ paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
+ offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
+ scroll = this.activeMenu.scrollTop();
+ elementHeight = this.activeMenu.height() - 26;
+ itemHeight = item.outerHeight();
+
+ if ( offset < 0 ) {
+ this.activeMenu.scrollTop( scroll + offset );
+ } else if ( offset + itemHeight > elementHeight ) {
+ this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
+ }
+ }
+ };
+ },
+ _renderMenu: function(ul, items) {
+ var currentCategory = "";
+ var widget = this;
+ widget.menu.bindings = $();
+ $.each(items, function(index, item) {
+ if (item.category && item.category !== currentCategory) {
+ ul.append("
+
+
diff --git a/deliveries/Javadoc/stylesheet.css b/deliveries/Javadoc/stylesheet.css
new file mode 100644
index 00000000..f71489f8
--- /dev/null
+++ b/deliveries/Javadoc/stylesheet.css
@@ -0,0 +1,1272 @@
+/*
+ * Javadoc style sheet
+ */
+
+@import url('resources/fonts/dejavu.css');
+
+/*
+ * These CSS custom properties (variables) define the core color and font
+ * properties used in this stylesheet.
+ */
+:root {
+ /* body, block and code fonts */
+ --body-font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
+ --block-font-family: 'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+ --code-font-family: 'DejaVu Sans Mono', monospace;
+ /* Base font sizes for body and code elements */
+ --body-font-size: 14px;
+ --code-font-size: 14px;
+ /* Text colors for body and block elements */
+ --body-text-color: #353833;
+ --block-text-color: #474747;
+ /* Background colors for various structural elements */
+ --body-background-color: #ffffff;
+ --section-background-color: #f8f8f8;
+ --detail-background-color: #ffffff;
+ /* Colors for navigation bar and table captions */
+ --navbar-background-color: #4D7A97;
+ --navbar-text-color: #ffffff;
+ /* Background color for subnavigation and various headers */
+ --subnav-background-color: #dee3e9;
+ /* Background and text colors for selected tabs and navigation items */
+ --selected-background-color: #f8981d;
+ --selected-text-color: #253441;
+ --selected-link-color: #1f389c;
+ /* Background colors for generated tables */
+ --even-row-color: #ffffff;
+ --odd-row-color: #eeeeef;
+ /* Text color for page title */
+ --title-color: #2c4557;
+ /* Text colors for links */
+ --link-color: #4A6782;
+ --link-color-active: #bb7a2a;
+ /* Snippet colors */
+ --snippet-background-color: #ebecee;
+ --snippet-text-color: var(--block-text-color);
+ --snippet-highlight-color: #f7c590;
+ /* Border colors for structural elements and user defined tables */
+ --border-color: #ededed;
+ --table-border-color: #000000;
+ /* Search input colors */
+ --search-input-background-color: #ffffff;
+ --search-input-text-color: #000000;
+ --search-input-placeholder-color: #909090;
+ /* Highlight color for active search tag target */
+ --search-tag-highlight-color: #ffff00;
+ /* Adjustments for icon and active background colors of copy-to-clipboard buttons */
+ --copy-icon-brightness: 100%;
+ --copy-button-background-color-active: rgba(168, 168, 176, 0.3);
+ /* Colors for invalid tag notifications */
+ --invalid-tag-background-color: #ffe6e6;
+ --invalid-tag-text-color: #000000;
+}
+/*
+ * Styles for individual HTML elements.
+ *
+ * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular
+ * HTML element throughout the page.
+ */
+body {
+ background-color:var(--body-background-color);
+ color:var(--body-text-color);
+ font-family:var(--body-font-family);
+ font-size:var(--body-font-size);
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+}
+iframe {
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+ overflow-y:scroll;
+ border:none;
+}
+a:link, a:visited {
+ text-decoration:none;
+ color:var(--link-color);
+}
+a[href]:hover, a[href]:focus {
+ text-decoration:none;
+ color:var(--link-color-active);
+}
+pre {
+ font-family:var(--code-font-family);
+ font-size:1em;
+}
+h1 {
+ font-size:1.428em;
+}
+h2 {
+ font-size:1.285em;
+}
+h3 {
+ font-size:1.14em;
+}
+h4 {
+ font-size:1.072em;
+}
+h5 {
+ font-size:1.001em;
+}
+h6 {
+ font-size:0.93em;
+}
+/* Disable font boosting for selected elements */
+h1, h2, h3, h4, h5, h6, div.member-signature {
+ max-height: 1000em;
+}
+ul {
+ list-style-type:disc;
+}
+code, tt {
+ font-family:var(--code-font-family);
+}
+:not(h1, h2, h3, h4, h5, h6) > code,
+:not(h1, h2, h3, h4, h5, h6) > tt {
+ font-size:var(--code-font-size);
+ padding-top:4px;
+ margin-top:8px;
+ line-height:1.4em;
+}
+dt code {
+ font-family:var(--code-font-family);
+ font-size:1em;
+ padding-top:4px;
+}
+.summary-table dt code {
+ font-family:var(--code-font-family);
+ font-size:1em;
+ vertical-align:top;
+ padding-top:4px;
+}
+sup {
+ font-size:8px;
+}
+button {
+ font-family: var(--body-font-family);
+ font-size: 1em;
+}
+/*
+ * Styles for HTML generated by javadoc.
+ *
+ * These are style classes that are used by the standard doclet to generate HTML documentation.
+ */
+
+/*
+ * Styles for document title and copyright.
+ */
+.about-language {
+ float:right;
+ padding:0 21px 8px 8px;
+ font-size:0.915em;
+ margin-top:-9px;
+ height:2.9em;
+}
+.legal-copy {
+ margin-left:.5em;
+}
+/*
+ * Styles for navigation bar.
+ */
+@media screen {
+ div.flex-box {
+ position:fixed;
+ display:flex;
+ flex-direction:column;
+ height: 100%;
+ width: 100%;
+ }
+ header.flex-header {
+ flex: 0 0 auto;
+ }
+ div.flex-content {
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+}
+.top-nav {
+ background-color:var(--navbar-background-color);
+ color:var(--navbar-text-color);
+ float:left;
+ width:100%;
+ clear:right;
+ min-height:2.8em;
+ padding:10px 0 0 0;
+ overflow:hidden;
+ font-size:0.857em;
+}
+button#navbar-toggle-button {
+ display:none;
+}
+ul.sub-nav-list-small {
+ display: none;
+}
+.sub-nav {
+ background-color:var(--subnav-background-color);
+ float:left;
+ width:100%;
+ overflow:hidden;
+ font-size:0.857em;
+}
+.sub-nav div {
+ clear:left;
+ float:left;
+ padding:6px;
+ text-transform:uppercase;
+}
+.sub-nav .sub-nav-list {
+ padding-top:4px;
+}
+ul.nav-list {
+ display:block;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.sub-nav-list {
+ float:left;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.nav-list li {
+ list-style:none;
+ float:left;
+ padding: 5px 6px;
+ text-transform:uppercase;
+}
+.sub-nav .nav-list-search {
+ float:right;
+ margin:0;
+ padding:6px;
+ clear:none;
+ text-align:right;
+ position:relative;
+}
+ul.sub-nav-list li {
+ list-style:none;
+ float:left;
+}
+.top-nav a:link, .top-nav a:active, .top-nav a:visited {
+ color:var(--navbar-text-color);
+ text-decoration:none;
+ text-transform:uppercase;
+}
+.top-nav a:hover {
+ color:var(--link-color-active);
+}
+.nav-bar-cell1-rev {
+ background-color:var(--selected-background-color);
+ color:var(--selected-text-color);
+ margin: auto 5px;
+}
+.skip-nav {
+ position:absolute;
+ top:auto;
+ left:-9999px;
+ overflow:hidden;
+}
+/*
+ * Hide navigation links and search box in print layout
+ */
+@media print {
+ ul.nav-list, div.sub-nav {
+ display:none;
+ }
+}
+/*
+ * Styles for page header.
+ */
+.title {
+ color:var(--title-color);
+ margin:10px 0;
+}
+.sub-title {
+ margin:5px 0 0 0;
+}
+ul.contents-list {
+ margin: 0 0 15px 0;
+ padding: 0;
+ list-style: none;
+}
+ul.contents-list li {
+ font-size:0.93em;
+}
+/*
+ * Styles for headings.
+ */
+body.class-declaration-page .summary h2,
+body.class-declaration-page .details h2,
+body.class-use-page h2,
+body.module-declaration-page .block-list h2 {
+ font-style: italic;
+ padding:0;
+ margin:15px 0;
+}
+body.class-declaration-page .summary h3,
+body.class-declaration-page .details h3,
+body.class-declaration-page .summary .inherited-list h2 {
+ background-color:var(--subnav-background-color);
+ border:1px solid var(--border-color);
+ margin:0 0 6px -8px;
+ padding:7px 5px;
+}
+/*
+ * Styles for page layout containers.
+ */
+main {
+ clear:both;
+ padding:10px 20px;
+ position:relative;
+}
+dl.notes > dt {
+ font-family: var(--body-font-family);
+ font-size:0.856em;
+ font-weight:bold;
+ margin:10px 0 0 0;
+ color:var(--body-text-color);
+}
+dl.notes > dd {
+ margin:5px 10px 10px 0;
+ font-size:1em;
+ font-family:var(--block-font-family)
+}
+dl.name-value > dt {
+ margin-left:1px;
+ font-size:1.1em;
+ display:inline;
+ font-weight:bold;
+}
+dl.name-value > dd {
+ margin:0 0 0 1px;
+ font-size:1.1em;
+ display:inline;
+}
+/*
+ * Styles for lists.
+ */
+li.circle {
+ list-style:circle;
+}
+ul.horizontal li {
+ display:inline;
+ font-size:0.9em;
+}
+div.inheritance {
+ margin:0;
+ padding:0;
+}
+div.inheritance div.inheritance {
+ margin-left:2em;
+}
+ul.block-list,
+ul.details-list,
+ul.member-list,
+ul.summary-list {
+ margin:10px 0 10px 0;
+ padding:0;
+}
+ul.block-list > li,
+ul.details-list > li,
+ul.member-list > li,
+ul.summary-list > li {
+ list-style:none;
+ margin-bottom:15px;
+ line-height:1.4;
+}
+ul.ref-list {
+ padding:0;
+ margin:0;
+}
+ul.ref-list > li {
+ list-style:none;
+}
+.summary-table dl, .summary-table dl dt, .summary-table dl dd {
+ margin-top:0;
+ margin-bottom:1px;
+}
+ul.tag-list, ul.tag-list-long {
+ padding-left: 0;
+ list-style: none;
+}
+ul.tag-list li {
+ display: inline;
+}
+ul.tag-list li:not(:last-child):after,
+ul.tag-list-long li:not(:last-child):after
+{
+ content: ", ";
+ white-space: pre-wrap;
+}
+ul.preview-feature-list {
+ list-style: none;
+ margin:0;
+ padding:0.1em;
+ line-height: 1.6em;
+}
+/*
+ * Styles for tables.
+ */
+.summary-table, .details-table {
+ width:100%;
+ border-spacing:0;
+ border:1px solid var(--border-color);
+ border-top:0;
+ padding:0;
+}
+.caption {
+ position:relative;
+ text-align:left;
+ background-repeat:no-repeat;
+ color:var(--selected-text-color);
+ clear:none;
+ overflow:hidden;
+ padding: 10px 0 0 1px;
+ margin:0;
+}
+.caption a:link, .caption a:visited {
+ color:var(--selected-link-color);
+}
+.caption a:hover,
+.caption a:active {
+ color:var(--navbar-text-color);
+}
+.caption span {
+ font-weight:bold;
+ white-space:nowrap;
+ padding:5px 12px 7px 12px;
+ display:inline-block;
+ float:left;
+ background-color:var(--selected-background-color);
+ border: none;
+ height:16px;
+}
+div.table-tabs {
+ padding:10px 0 0 1px;
+ margin:10px 0 0 0;
+}
+div.table-tabs > button {
+ border: none;
+ cursor: pointer;
+ padding: 5px 12px 7px 12px;
+ font-weight: bold;
+ margin-right: 8px;
+}
+div.table-tabs > .active-table-tab {
+ background: var(--selected-background-color);
+ color: var(--selected-text-color);
+}
+div.table-tabs > button.table-tab {
+ background: var(--navbar-background-color);
+ color: var(--navbar-text-color);
+}
+.two-column-search-results {
+ display: grid;
+ grid-template-columns: minmax(400px, max-content) minmax(400px, auto);
+}
+div.checkboxes {
+ line-height: 2em;
+}
+div.checkboxes > span {
+ margin-left: 10px;
+}
+div.checkboxes > label {
+ margin-left: 8px;
+ white-space: nowrap;
+}
+div.checkboxes > label > input {
+ margin: 0 2px;
+}
+.two-column-summary {
+ display: grid;
+ grid-template-columns: minmax(25%, max-content) minmax(25%, auto);
+}
+.three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(20%, max-content) minmax(20%, auto);
+}
+.three-column-release-summary {
+ display: grid;
+ grid-template-columns: minmax(40%, max-content) minmax(10%, max-content) minmax(40%, auto);
+}
+.four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(15%, max-content) minmax(15%, max-content) minmax(15%, auto);
+}
+@media screen and (max-width: 1000px) {
+ .four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(15%, auto);
+ }
+}
+@media screen and (max-width: 800px) {
+ .two-column-search-results {
+ display: grid;
+ grid-template-columns: minmax(40%, max-content) minmax(40%, auto);
+ }
+ .three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(25%, auto);
+ }
+ .three-column-release-summary {
+ display: grid;
+ grid-template-columns: minmax(70%, max-content) minmax(30%, max-content)
+ }
+ .three-column-summary .col-last,
+ .three-column-release-summary .col-last{
+ grid-column-end: span 2;
+ }
+}
+@media screen and (max-width: 600px) {
+ .two-column-summary {
+ display: grid;
+ grid-template-columns: 1fr;
+ }
+}
+.summary-table > div, .details-table > div {
+ text-align:left;
+ padding: 8px 3px 3px 7px;
+ overflow-x: auto;
+ scrollbar-width: thin;
+}
+.col-first, .col-second, .col-last, .col-constructor-name, .col-summary-item-name {
+ vertical-align:top;
+ padding-right:0;
+ padding-top:8px;
+ padding-bottom:3px;
+}
+.table-header {
+ background:var(--subnav-background-color);
+ font-weight: bold;
+}
+/* Sortable table columns */
+.table-header[onclick] {
+ cursor: pointer;
+}
+.table-header[onclick]::after {
+ content:"";
+ display:inline-block;
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+ background-size:100% 100%;
+ width:9px;
+ height:14px;
+ margin-left:4px;
+ margin-bottom:-3px;
+}
+.table-header[onclick].sort-asc::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+
+}
+.table-header[onclick].sort-desc::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+}
+.col-first, .col-first {
+ font-size:0.93em;
+}
+.col-second, .col-second, .col-last, .col-constructor-name, .col-summary-item-name, .col-last {
+ font-size:0.93em;
+}
+.col-first, .col-second, .col-constructor-name {
+ vertical-align:top;
+ overflow: auto;
+}
+.col-last {
+ white-space:normal;
+}
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-constructor-name a:link, .col-constructor-name a:visited,
+.col-summary-item-name a:link, .col-summary-item-name a:visited {
+ font-weight:bold;
+}
+.even-row-color, .even-row-color .table-header {
+ background-color:var(--even-row-color);
+}
+.odd-row-color, .odd-row-color .table-header {
+ background-color:var(--odd-row-color);
+}
+/*
+ * Styles for contents.
+ */
+div.block {
+ font-size:var(--body-font-size);
+ font-family:var(--block-font-family);
+}
+.col-last div {
+ padding-top:0;
+}
+.col-last a {
+ padding-bottom:3px;
+}
+.module-signature,
+.package-signature,
+.type-signature,
+.member-signature {
+ font-family:var(--code-font-family);
+ font-size:1em;
+ margin:14px 0;
+ white-space: pre-wrap;
+}
+.module-signature,
+.package-signature,
+.type-signature {
+ margin-top: 0;
+}
+.member-signature .type-parameters-long,
+.member-signature .parameters,
+.member-signature .exceptions {
+ display: inline-block;
+ vertical-align: top;
+ white-space: pre;
+}
+.member-signature .type-parameters {
+ white-space: normal;
+}
+/*
+ * Styles for formatting effect.
+ */
+.source-line-no {
+ /* Color of line numbers in source pages can be set via custom property below */
+ color:var(--source-linenumber-color, green);
+ padding:0 30px 0 0;
+}
+.block {
+ display:block;
+ margin:0 10px 5px 0;
+ color:var(--block-text-color);
+}
+.deprecated-label, .description-from-type-label, .implementation-label, .member-name-link,
+.module-label-in-package, .module-label-in-type, .package-label-in-type,
+.package-hierarchy-label, .type-name-label, .type-name-link, .search-tag-link, .preview-label {
+ font-weight:bold;
+}
+.deprecation-comment, .help-footnote, .preview-comment {
+ font-style:italic;
+}
+.deprecation-block {
+ font-size:1em;
+ font-family:var(--block-font-family);
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+.preview-block {
+ font-size:1em;
+ font-family:var(--block-font-family);
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+div.block div.deprecation-comment {
+ font-style:normal;
+}
+details.invalid-tag, span.invalid-tag {
+ font-size:1em;
+ font-family:var(--block-font-family);
+ color: var(--invalid-tag-text-color);
+ background: var(--invalid-tag-background-color);
+ border: thin solid var(--table-border-color);
+ border-radius:2px;
+ padding: 2px 4px;
+ display:inline-block;
+}
+details summary {
+ cursor: pointer;
+}
+/*
+ * Styles specific to HTML5 elements.
+ */
+main, nav, header, footer, section {
+ display:block;
+}
+/*
+ * Styles for javadoc search.
+ */
+.ui-state-active {
+ /* Overrides the color of selection used in jQuery UI */
+ background: var(--selected-background-color);
+ border: 1px solid var(--selected-background-color);
+ color: var(--selected-text-color);
+}
+.ui-autocomplete-category {
+ font-weight:bold;
+ font-size:15px;
+ padding:7px 0 7px 3px;
+ background-color:var(--navbar-background-color);
+ color:var(--navbar-text-color);
+}
+.ui-autocomplete {
+ max-height:85%;
+ max-width:65%;
+ overflow-y:auto;
+ overflow-x:auto;
+ scrollbar-width: thin;
+ white-space:nowrap;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+}
+ul.ui-autocomplete {
+ position:fixed;
+ z-index:1;
+ background-color: var(--body-background-color);
+}
+ul.ui-autocomplete li {
+ float:left;
+ clear:both;
+ min-width:100%;
+}
+ul.ui-autocomplete li.ui-static-link {
+ position:sticky;
+ bottom:0;
+ left:0;
+ background: var(--subnav-background-color);
+ padding: 5px 0;
+ font-family: var(--body-font-family);
+ font-size: 0.93em;
+ font-weight: bolder;
+ z-index: 2;
+}
+li.ui-static-link a, li.ui-static-link a:visited {
+ text-decoration:none;
+ color:var(--link-color);
+ float:right;
+ margin-right:20px;
+}
+.ui-autocomplete .result-item {
+ font-size: inherit;
+}
+.ui-autocomplete .result-highlight {
+ font-weight:bold;
+}
+#search-input, #page-search-input {
+ background-image:url('resources/glass.png');
+ background-size:13px;
+ background-repeat:no-repeat;
+ background-position:2px 3px;
+ background-color: var(--search-input-background-color);
+ color: var(--search-input-text-color);
+ border-color: var(--border-color);
+ padding-left:20px;
+ width: 250px;
+ margin: 0;
+}
+#search-input {
+ margin-left: 4px;
+}
+#reset-button {
+ background-color: transparent;
+ background-image:url('resources/x.png');
+ background-repeat:no-repeat;
+ background-size:contain;
+ border:0;
+ border-radius:0;
+ width:12px;
+ height:12px;
+ position:absolute;
+ right:12px;
+ top:10px;
+ font-size:0;
+}
+::placeholder {
+ color:var(--search-input-placeholder-color);
+ opacity: 1;
+}
+.search-tag-desc-result {
+ font-style:italic;
+ font-size:11px;
+}
+.search-tag-holder-result {
+ font-style:italic;
+ font-size:12px;
+}
+.search-tag-result:target {
+ background-color:var(--search-tag-highlight-color);
+}
+details.page-search-details {
+ display: inline-block;
+}
+div#result-container {
+ font-size: 1em;
+}
+div#result-container a.search-result-link {
+ padding: 0;
+ margin: 4px 0;
+ width: 100%;
+}
+#result-container .result-highlight {
+ font-weight:bolder;
+}
+.page-search-info {
+ background-color: var(--subnav-background-color);
+ border-radius: 3px;
+ border: 0 solid var(--border-color);
+ padding: 0 8px;
+ overflow: hidden;
+ height: 0;
+ transition: all 0.2s ease;
+}
+div.table-tabs > button.table-tab {
+ background: var(--navbar-background-color);
+ color: var(--navbar-text-color);
+}
+.page-search-header {
+ padding: 5px 12px 7px 12px;
+ font-weight: bold;
+ margin-right: 3px;
+ background-color:var(--navbar-background-color);
+ color:var(--navbar-text-color);
+ display: inline-block;
+}
+button.page-search-header {
+ border: none;
+ cursor: pointer;
+}
+span#page-search-link {
+ text-decoration: underline;
+}
+.module-graph span, .sealed-graph span {
+ display:none;
+ position:absolute;
+}
+.module-graph:hover span, .sealed-graph:hover span {
+ display:block;
+ margin: -100px 0 0 100px;
+ z-index: 1;
+}
+.inherited-list {
+ margin: 10px 0 10px 0;
+}
+section.class-description {
+ line-height: 1.4;
+}
+.summary section[class$="-summary"], .details section[class$="-details"],
+.class-uses .detail, .serialized-class-details {
+ padding: 0 20px 5px 10px;
+ border: 1px solid var(--border-color);
+ background-color: var(--section-background-color);
+}
+.inherited-list, section[class$="-details"] .detail {
+ padding:0 0 5px 8px;
+ background-color:var(--detail-background-color);
+ border:none;
+}
+.vertical-separator {
+ padding: 0 5px;
+}
+ul.help-section-list {
+ margin: 0;
+}
+ul.help-subtoc > li {
+ display: inline-block;
+ padding-right: 5px;
+ font-size: smaller;
+}
+ul.help-subtoc > li::before {
+ content: "\2022" ;
+ padding-right:2px;
+}
+.help-note {
+ font-style: italic;
+}
+/*
+ * Indicator icon for external links.
+ */
+main a[href*="://"]::after {
+ content:"";
+ display:inline-block;
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+ background-size:100% 100%;
+ width:7px;
+ height:7px;
+ margin-left:2px;
+ margin-bottom:4px;
+}
+main a[href*="://"]:hover::after,
+main a[href*="://"]:focus::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+}
+/*
+ * Styles for header/section anchor links
+ */
+a.anchor-link {
+ opacity: 0;
+ transition: opacity 0.1s;
+}
+:hover > a.anchor-link {
+ opacity: 80%;
+}
+a.anchor-link:hover,
+a.anchor-link:focus-visible,
+a.anchor-link.visible {
+ opacity: 100%;
+}
+a.anchor-link > img {
+ width: 0.9em;
+ height: 0.9em;
+}
+/*
+ * Styles for copy-to-clipboard buttons
+ */
+button.copy {
+ opacity: 70%;
+ border: none;
+ border-radius: 3px;
+ position: relative;
+ background:none;
+ transition: opacity 0.3s;
+ cursor: pointer;
+}
+:hover > button.copy {
+ opacity: 80%;
+}
+button.copy:hover,
+button.copy:active,
+button.copy:focus-visible,
+button.copy.visible {
+ opacity: 100%;
+}
+button.copy img {
+ position: relative;
+ background: none;
+ filter: brightness(var(--copy-icon-brightness));
+}
+button.copy:active {
+ background-color: var(--copy-button-background-color-active);
+}
+button.copy span {
+ color: var(--body-text-color);
+ position: relative;
+ top: -0.1em;
+ transition: all 0.1s;
+ font-size: 0.76rem;
+ line-height: 1.2em;
+ opacity: 0;
+}
+button.copy:hover span,
+button.copy:focus-visible span,
+button.copy.visible span {
+ opacity: 100%;
+}
+/* search page copy button */
+button#page-search-copy {
+ margin-left: 0.4em;
+ padding:0.3em;
+ top:0.13em;
+}
+button#page-search-copy img {
+ width: 1.2em;
+ height: 1.2em;
+ padding: 0.01em 0;
+ top: 0.15em;
+}
+button#page-search-copy span {
+ color: var(--body-text-color);
+ line-height: 1.2em;
+ padding: 0.2em;
+ top: -0.18em;
+}
+div.page-search-info:hover button#page-search-copy span {
+ opacity: 100%;
+}
+/* snippet copy button */
+button.snippet-copy {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ height: 1.7em;
+ padding: 2px;
+}
+button.snippet-copy img {
+ width: 18px;
+ height: 18px;
+ padding: 0.05em 0;
+}
+button.snippet-copy span {
+ line-height: 1.2em;
+ padding: 0.2em;
+ position: relative;
+ top: -0.5em;
+}
+div.snippet-container:hover button.snippet-copy span {
+ opacity: 100%;
+}
+/*
+ * Styles for user-provided tables.
+ *
+ * borderless:
+ * No borders, vertical margins, styled caption.
+ * This style is provided for use with existing doc comments.
+ * In general, borderless tables should not be used for layout purposes.
+ *
+ * plain:
+ * Plain borders around table and cells, vertical margins, styled caption.
+ * Best for small tables or for complex tables for tables with cells that span
+ * rows and columns, when the "striped" style does not work well.
+ *
+ * striped:
+ * Borders around the table and vertical borders between cells, striped rows,
+ * vertical margins, styled caption.
+ * Best for tables that have a header row, and a body containing a series of simple rows.
+ */
+
+table.borderless,
+table.plain,
+table.striped {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+table.borderless > caption,
+table.plain > caption,
+table.striped > caption {
+ font-weight: bold;
+ font-size: smaller;
+}
+table.borderless th, table.borderless td,
+table.plain th, table.plain td,
+table.striped th, table.striped td {
+ padding: 2px 5px;
+}
+table.borderless,
+table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th,
+table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td {
+ border: none;
+}
+table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr {
+ background-color: transparent;
+}
+table.plain {
+ border-collapse: collapse;
+ border: 1px solid var(--table-border-color);
+}
+table.plain > thead > tr, table.plain > tbody tr, table.plain > tr {
+ background-color: transparent;
+}
+table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th,
+table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td {
+ border: 1px solid var(--table-border-color);
+}
+table.striped {
+ border-collapse: collapse;
+ border: 1px solid var(--table-border-color);
+}
+table.striped > thead {
+ background-color: var(--subnav-background-color);
+}
+table.striped > thead > tr > th, table.striped > thead > tr > td {
+ border: 1px solid var(--table-border-color);
+}
+table.striped > tbody > tr:nth-child(even) {
+ background-color: var(--odd-row-color)
+}
+table.striped > tbody > tr:nth-child(odd) {
+ background-color: var(--even-row-color)
+}
+table.striped > tbody > tr > th, table.striped > tbody > tr > td {
+ border-left: 1px solid var(--table-border-color);
+ border-right: 1px solid var(--table-border-color);
+}
+table.striped > tbody > tr > th {
+ font-weight: normal;
+}
+/**
+ * Tweak style for small screens.
+ */
+@media screen and (max-width: 920px) {
+ header.flex-header {
+ max-height: 100vh;
+ overflow-y: auto;
+ }
+ div#navbar-top {
+ height: 2.8em;
+ transition: height 0.35s ease;
+ }
+ ul.nav-list {
+ display: block;
+ width: 40%;
+ float:left;
+ clear: left;
+ margin: 10px 0 0 0;
+ padding: 0;
+ }
+ ul.nav-list li {
+ float: none;
+ padding: 6px;
+ margin-left: 10px;
+ margin-top: 2px;
+ }
+ ul.sub-nav-list-small {
+ display:block;
+ height: 100%;
+ width: 50%;
+ float: right;
+ clear: right;
+ background-color: var(--subnav-background-color);
+ color: var(--body-text-color);
+ margin: 6px 0 0 0;
+ padding: 0;
+ }
+ ul.sub-nav-list-small ul {
+ padding-left: 20px;
+ }
+ ul.sub-nav-list-small a:link, ul.sub-nav-list-small a:visited {
+ color:var(--link-color);
+ }
+ ul.sub-nav-list-small a:hover {
+ color:var(--link-color-active);
+ }
+ ul.sub-nav-list-small li {
+ list-style:none;
+ float:none;
+ padding: 6px;
+ margin-top: 1px;
+ text-transform:uppercase;
+ }
+ ul.sub-nav-list-small > li {
+ margin-left: 10px;
+ }
+ ul.sub-nav-list-small li p {
+ margin: 5px 0;
+ }
+ div#navbar-sub-list {
+ display: none;
+ }
+ .top-nav a:link, .top-nav a:active, .top-nav a:visited {
+ display: block;
+ }
+ button#navbar-toggle-button {
+ width: 3.4em;
+ height: 2.8em;
+ background-color: transparent;
+ display: block;
+ float: left;
+ border: 0;
+ margin: 0 10px;
+ cursor: pointer;
+ font-size: 10px;
+ }
+ button#navbar-toggle-button .nav-bar-toggle-icon {
+ display: block;
+ width: 24px;
+ height: 3px;
+ margin: 1px 0 4px 0;
+ border-radius: 2px;
+ transition: all 0.1s;
+ background-color: var(--navbar-text-color);
+ }
+ button#navbar-toggle-button.expanded span.nav-bar-toggle-icon:nth-child(1) {
+ transform: rotate(45deg);
+ transform-origin: 10% 10%;
+ width: 26px;
+ }
+ button#navbar-toggle-button.expanded span.nav-bar-toggle-icon:nth-child(2) {
+ opacity: 0;
+ }
+ button#navbar-toggle-button.expanded span.nav-bar-toggle-icon:nth-child(3) {
+ transform: rotate(-45deg);
+ transform-origin: 10% 90%;
+ width: 26px;
+ }
+}
+@media screen and (max-width: 800px) {
+ .about-language {
+ padding-right: 16px;
+ }
+ ul.nav-list li {
+ margin-left: 5px;
+ }
+ ul.sub-nav-list-small > li {
+ margin-left: 5px;
+ }
+ main {
+ padding: 10px;
+ }
+ .summary section[class$="-summary"], .details section[class$="-details"],
+ .class-uses .detail, .serialized-class-details {
+ padding: 0 8px 5px 8px;
+ }
+ body {
+ -webkit-text-size-adjust: none;
+ }
+}
+@media screen and (max-width: 400px) {
+ .about-language {
+ font-size: 10px;
+ padding-right: 12px;
+ }
+}
+@media screen and (max-width: 400px) {
+ .nav-list-search {
+ width: 94%;
+ }
+ #search-input, #page-search-input {
+ width: 70%;
+ }
+}
+@media screen and (max-width: 320px) {
+ .nav-list-search > label {
+ display: none;
+ }
+ .nav-list-search {
+ width: 90%;
+ }
+ #search-input, #page-search-input {
+ width: 80%;
+ }
+}
+
+pre.snippet {
+ background-color: var(--snippet-background-color);
+ color: var(--snippet-text-color);
+ padding: 10px;
+ margin: 12px 0;
+ overflow: auto;
+ white-space: pre;
+}
+div.snippet-container {
+ position: relative;
+}
+@media screen and (max-width: 800px) {
+ pre.snippet {
+ padding-top: 26px;
+ }
+ button.snippet-copy {
+ top: 4px;
+ right: 4px;
+ }
+}
+pre.snippet .italic {
+ font-style: italic;
+}
+pre.snippet .bold {
+ font-weight: bold;
+}
+pre.snippet .highlighted {
+ background-color: var(--snippet-highlight-color);
+ border-radius: 10%;
+}
diff --git a/deliveries/Javadoc/tag-search-index.js b/deliveries/Javadoc/tag-search-index.js
new file mode 100644
index 00000000..f38b3cb3
--- /dev/null
+++ b/deliveries/Javadoc/tag-search-index.js
@@ -0,0 +1 @@
+tagSearchIndex = [{"l":"Serialized Form","h":"","u":"serialized-form.html"}];updateSearchResults();
\ No newline at end of file
diff --git a/deliveries/Javadoc/type-search-index.js b/deliveries/Javadoc/type-search-index.js
new file mode 100644
index 00000000..b8e5f9dc
--- /dev/null
+++ b/deliveries/Javadoc/type-search-index.js
@@ -0,0 +1 @@
+typeSearchIndex = [{"p":"it.polimi.ingsw.network.messages.actions","l":"ActionMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"AfterDrawState"},{"p":"it.polimi.ingsw.gamemodel","l":"AfterMoveState"},{"l":"All Classes and Interfaces","u":"allclasses-index.html"},{"p":"it.polimi.ingsw.exceptions","l":"AlreadyUsedUsernameException"},{"p":"it.polimi.ingsw.utils","l":"AvailableMatch"},{"p":"it.polimi.ingsw.network.messages.responses","l":"AvailableMatchesMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"Board"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","l":"BoardPane"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"BoardPosition"},{"p":"it.polimi.ingsw.gamemodel","l":"Card"},{"p":"it.polimi.ingsw.exceptions","l":"CardException"},{"p":"it.polimi.ingsw.gamemodel","l":"CardFace"},{"p":"it.polimi.ingsw.utils","l":"CardJsonParser"},{"p":"it.polimi.ingsw.utils","l":"CardsManager"},{"p":"it.polimi.ingsw.utils","l":"CardsSerializer"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","l":"CardView"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"ChatPaneController"},{"p":"it.polimi.ingsw.network.messages.actions","l":"ChooseInitialCardSideMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"ChooseInitialSideState"},{"p":"it.polimi.ingsw.network.messages.actions","l":"ChooseSecretObjectiveMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"ChooseSecretObjectiveState"},{"p":"it.polimi.ingsw.exceptions","l":"ChosenMatchException"},{"p":"it.polimi.ingsw.client.frontend","l":"ClientBoard"},{"p":"it.polimi.ingsw.network.tcp","l":"ClientListener"},{"p":"it.polimi.ingsw.network.tcp","l":"ClientReceiver"},{"p":"it.polimi.ingsw.gamemodel","l":"Color"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"ConnectionSceneController"},{"p":"it.polimi.ingsw.gamemodel","l":"Corner"},{"p":"it.polimi.ingsw.network.messages.actions","l":"CreateMatchMessage"},{"p":"it.polimi.ingsw.utils","l":"DeckCreator"},{"p":"it.polimi.ingsw.exceptions","l":"DeckException"},{"p":"it.polimi.ingsw.network.messages.actions","l":"DrawCardMessage"},{"p":"it.polimi.ingsw.network.messages.actions","l":"DrawInitialCardMessage"},{"p":"it.polimi.ingsw.network.messages.actions","l":"DrawSecretObjectivesMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"DrawSource"},{"p":"it.polimi.ingsw.network.messages.errors","l":"ErrorMessage"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"ErrorSceneController"},{"p":"it.polimi.ingsw.gamemodel","l":"FinalState"},{"p":"it.polimi.ingsw.gamemodel","l":"GameDeck"},{"p":"it.polimi.ingsw.network.messages.actions","l":"GetAvailableMatchesMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"GoldCard"},{"p":"it.polimi.ingsw.client.frontend.gui","l":"GraphicalApplication"},{"p":"it.polimi.ingsw.client.frontend","l":"GraphicalView"},{"p":"it.polimi.ingsw.client.frontend.gui","l":"GraphicalViewGUI"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"GraphicalViewTUI"},{"p":"it.polimi.ingsw.utils","l":"GuiUtil"},{"p":"it.polimi.ingsw.exceptions","l":"HandException"},{"p":"it.polimi.ingsw.gamemodel","l":"InitialCard"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"InputHandler"},{"p":"it.polimi.ingsw.exceptions","l":"InvalidPlayerException"},{"p":"it.polimi.ingsw.exceptions","l":"InvalidResourceException"},{"p":"it.polimi.ingsw.network.tcp","l":"IOHandler"},{"p":"it.polimi.ingsw.network.messages.actions","l":"JoinMatchMessage"},{"p":"it.polimi.ingsw.client.frontend","l":"LastRequest"},{"p":"it.polimi.ingsw.utils","l":"LeaderboardEntry"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"LobbySceneController"},{"p":"it.polimi.ingsw.gamemodel","l":"Match"},{"p":"it.polimi.ingsw.network.messages.responses","l":"MatchFinishedMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"MatchObserver"},{"p":"it.polimi.ingsw.gamemodel","l":"MatchObserverCallable"},{"p":"it.polimi.ingsw.network.messages.responses","l":"MatchResumedMessage"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"MatchSceneController"},{"p":"it.polimi.ingsw.network.messages.responses","l":"MatchStartedMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"MatchState"},{"p":"it.polimi.ingsw.client.frontend","l":"MatchStatus"},{"p":"it.polimi.ingsw.server","l":"MatchStatusObserver"},{"p":"it.polimi.ingsw.network.messages","l":"Message"},{"p":"it.polimi.ingsw.utils","l":"MessageJsonParser"},{"p":"it.polimi.ingsw.client.network","l":"NetworkHandler"},{"p":"it.polimi.ingsw.client.network","l":"NetworkHandlerRMI"},{"p":"it.polimi.ingsw.client.network","l":"NetworkHandlerTCP"},{"p":"it.polimi.ingsw.gamemodel","l":"NextTurnState"},{"p":"it.polimi.ingsw.gamemodel","l":"Objective"},{"p":"it.polimi.ingsw.utils","l":"Pair"},{"p":"it.polimi.ingsw.gamemodel","l":"PlacedCard"},{"p":"it.polimi.ingsw.utils","l":"PlacedCardRecord"},{"p":"it.polimi.ingsw.gamemodel","l":"PlacementOutcome"},{"p":"it.polimi.ingsw.client.frontend.gui.nodes","l":"PlateauPane"},{"p":"it.polimi.ingsw.gamemodel","l":"PlayableCard"},{"p":"it.polimi.ingsw.network.messages.actions","l":"PlayCardMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"Player"},{"p":"it.polimi.ingsw.controllers","l":"PlayerController"},{"p":"it.polimi.ingsw.controllers","l":"PlayerControllerRMI"},{"p":"it.polimi.ingsw.controllers","l":"PlayerControllerRMIInterface"},{"p":"it.polimi.ingsw.controllers","l":"PlayerControllerTCP"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"PlayerControls"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"PlayerTabController"},{"p":"it.polimi.ingsw.gamemodel","l":"PositionRequirement"},{"p":"it.polimi.ingsw.gamemodel","l":"QuantityRequirement"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"RankingSceneController"},{"p":"it.polimi.ingsw.client.network","l":"RemoteViewInterface"},{"p":"it.polimi.ingsw.utils","l":"RequestStatus"},{"p":"it.polimi.ingsw.gamemodel","l":"Requirement"},{"p":"it.polimi.ingsw.gamemodel","l":"ResourceCard"},{"p":"it.polimi.ingsw.network.messages.responses","l":"ResponseMessage"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"SceneController"},{"p":"it.polimi.ingsw.network.messages.actions","l":"SendBroadcastTextMessage"},{"p":"it.polimi.ingsw.network.messages.actions","l":"SendPrivateTextMessage"},{"p":"it.polimi.ingsw.server","l":"Server"},{"p":"it.polimi.ingsw.server","l":"ServerRMIInterface"},{"p":"it.polimi.ingsw.client.frontend","l":"ShownCard"},{"p":"it.polimi.ingsw.gamemodel","l":"Side"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneChoseSecretObjectiveMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneDrewCardMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneDrewInitialCardMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneDrewSecretObjectivesMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneJoinedMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeonePlayedCardMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneQuitMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneSentBroadcastTextMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneSentPrivateTextMessage"},{"p":"it.polimi.ingsw.network.messages.responses","l":"SomeoneSetInitialSideMessage"},{"p":"it.polimi.ingsw.gamemodel","l":"Symbol"},{"p":"it.polimi.ingsw.network.tcp","l":"TCPServer"},{"p":"it.polimi.ingsw.utils","l":"TUICardParser"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"TuiPrinter"},{"p":"it.polimi.ingsw.client.frontend.tui","l":"ValidPositions"},{"p":"it.polimi.ingsw.client.frontend.gui.controllers","l":"WaitingSceneController"},{"p":"it.polimi.ingsw.gamemodel","l":"WaitState"},{"p":"it.polimi.ingsw.exceptions","l":"WrongChoiceException"},{"p":"it.polimi.ingsw.exceptions","l":"WrongInputFormatException"},{"p":"it.polimi.ingsw.exceptions","l":"WrongNameException"},{"p":"it.polimi.ingsw.exceptions","l":"WrongStateException"},{"p":"it.polimi.ingsw.exceptions","l":"WrongTurnException"}];updateSearchResults();
\ No newline at end of file
diff --git a/deliveries/PeerReview/PeerReview1-UML.md b/deliveries/Peer reviews/Peer-Review1-UML.md
similarity index 72%
rename from deliveries/PeerReview/PeerReview1-UML.md
rename to deliveries/Peer reviews/Peer-Review1-UML.md
index 40120739..178077d8 100644
--- a/deliveries/PeerReview/PeerReview1-UML.md
+++ b/deliveries/Peer reviews/Peer-Review1-UML.md
@@ -6,8 +6,8 @@ Valutazione del class diagram UML del gruppo AM17.
## Lati positivi
### Generali
- Nel modello è stata prevista a livello concettuale, tramite la classe `GameResources`, la presenza di carte che esistono nel gioco indipendentemente dall'istanza di `GameModel` che le utilizza.
-La differenza è evidenziata dal fatto che `GameResources` presenta liste di `PlaceableCard`, mentre il `GameModel` presenta dei deck di carte separati
-(che supponiamo prendere le carta proprio da quelle List).
+ La differenza è evidenziata dal fatto che `GameResources` presenta liste di `PlaceableCard`, mentre il `GameModel` presenta dei deck di carte separati
+ (che supponiamo prendere le carta proprio da quelle List).
### GameController
- `GameController` rende molto chiaro quali metodi sono mostrati all'esterno e delinea chiaramente le interazioni dell'utente con il gioco.
@@ -23,7 +23,7 @@ La differenza è evidenziata dal fatto che `GameResources` presenta liste di `Pl
### ObjectiveCard
- La scelta di creare una sottoclasse per ogni possibile obiettivo risulta troppo frammentaria (infatti sono presenti 8 classi con differenze marginali).
-Inoltre troviamo la soluzione poco scalabile (l'inserimento di nuove carte obiettivo implicherebbe la creazione di classi ex novo).
+ Inoltre troviamo la soluzione poco scalabile (l'inserimento di nuove carte obiettivo implicherebbe la creazione di classi ex novo).
- L'attributo `points` è presente in tutte le sottoclassi di `ObjectiveCard`, e quindi potrebbe essere spostato nella sopraclasse.
### CardCorner
@@ -32,13 +32,13 @@ Inoltre troviamo la soluzione poco scalabile (l'inserimento di nuove carte obiet
### PlaceableCard
- Supponiamo che esista il metodo `getPermanentResources(...)` in `Back`, solo a fine esemplificativo.
-Gli attributi `front` e `back` di `PlaceableCard` sono di tipo statico `Side` dunque non è possibile accedere ai metodi unici delle sottoclassi senza effettuare.
-un casting (in Back non sarebbe possibile, invocare getPermanentResources(...)). Una possibile soluzione a questo problema è rendere gli attributi di `PlaceableCard`
-(e relativi getter) `front` e `back` rispettivamente di tipo statico `Front` e `Back`.
+ Gli attributi `front` e `back` di `PlaceableCard` sono di tipo statico `Side` dunque non è possibile accedere ai metodi unici delle sottoclassi senza effettuare.
+ un casting (in Back non sarebbe possibile, invocare getPermanentResources(...)). Una possibile soluzione a questo problema è rendere gli attributi di `PlaceableCard`
+ (e relativi getter) `front` e `back` rispettivamente di tipo statico `Front` e `Back`.
- Ci sentiamo di condividere un'osservazione: la classe `PlaceableCard` sembra abbia un po' mischiato in sé la struttura
-di una carta generica e di una carta piazzata; come denotato dalla presenza degli attributi `front` e `back` (appartenenti al concetto di carta generica)
-e `currSide` (appartenente al concetto di carta piazzata).
+ di una carta generica e di una carta piazzata; come denotato dalla presenza degli attributi `front` e `back` (appartenenti al concetto di carta generica)
+ e `currSide` (appartenente al concetto di carta piazzata).
- In `GoldenCard` non abbiamo identificato con quale meccanismo vengono calcolati i punti in base al numero di angoli coperti.
### GameModel
@@ -47,7 +47,7 @@ e `currSide` (appartenente al concetto di carta piazzata).
### PlayerModel
- La scelta di utilizzare la classe `Token` per rappresentare il colore del player (o la sua posizione, non risulta molto chiaro dal solo UML), non ci è sembrata una soluzione efficace
-in quanto tale classe ha solo un attributo, che potrebbe essere messo all'interno di `Player`.
+ in quanto tale classe ha solo un attributo, che potrebbe essere messo all'interno di `Player`.
- L'attributo `objectiveToChoose` risulta non necessario, in quanto esiste già l'attributo `secretObjective`.
### GameController
@@ -58,13 +58,13 @@ A differenza dalla soluzione adottata dal gruppo AM17, noi abbiamo preferito opt
- Nella nostra architettura non abbiamo fatto utilizzo di ID per le varie entità del gioco, tuttavia riteniamo probabile che l'introduzione di questi possa tornare utile nelle fasi successive dello sviluppo. Soprattutto nella comunicazione client-server, potrà tornare utile al fine di evitare il trasferimento di interi oggetti durante ogni transazione (e.g. ogni player deve comunicare al server quale carta ha piazzato inviando l'intero oggetto carta, oppure, specificatamente nella nostra architettura, inviando l'intero oggetto `Board`); tale scambio di oggetti potrebbe essere fatto durante la sola prima interazione fra client e server.
- Per maggiore scalabilità e chiarezza, abbiamo utilizzato in maniera più intensiva le `Enumeration`, sfruttando la possibilità di generare subset tramite metodi statici.
-Ad esempio, l'enumeration `Symbol` contiene tutti i simboli presenti nel gioco, e definisce dei metodi per ottenere i subset validi per ciascuna occasione
+ Ad esempio, l'enumeration `Symbol` contiene tutti i simboli presenti nel gioco, e definisce dei metodi per ottenere i subset validi per ciascuna occasione
- Abbiamo implementato una gestione della partita di gioco attraverso un modello a macchina a stati finiti (design pattern `State`), al fine di distinguere nettamente le fasi di gioco.
- Nella nostra architettura gli obiettivi richiedono un `Requirement` (utilizzato anche per il piazzamento delle carte oro), che si suddivide in due sottoclassi:
-`QuantityRequirement` per i requisiti relativi alle risorse e `PositionRequirement` per i requisiti posizionali, evitando così di creare una classe per ogni posizione possibile.
+ `QuantityRequirement` per i requisiti relativi alle risorse e `PositionRequirement` per i requisiti posizionali, evitando così di creare una classe per ogni posizione possibile.
- Abbiamo deciso di esporre all'esterno del model le classi `Match` e `Player`. In questo modo i Controller relativi ai giocatori interagiscono con il solo oggetto `Player`,
-mentre il gioco viene gestito in `Match`.
+ mentre il gioco viene gestito in `Match`.
- Il concetto di "carta piazzata" e "carta giocabile" sono da noi considerati diversi e vengono pertanto divisi: abbiamo creato una classe `Card`,
-da cui ereditano `InitialCard`, `GoldCard` e `ResourceCard`, e una classe `PlacedCard` composta dal turno in cui viene piazzata e dalla carta che rappresenta.
-In questo modo le istanze di `Card` non dipendono da un particolare `Match`, mentre le `PlacedCard` fungono da wrapper di `Card` strettamente dipendenti dal `Match`.
+ da cui ereditano `InitialCard`, `GoldCard` e `ResourceCard`, e una classe `PlacedCard` composta dal turno in cui viene piazzata e dalla carta che rappresenta.
+ In questo modo le istanze di `Card` non dipendono da un particolare `Match`, mentre le `PlacedCard` fungono da wrapper di `Card` strettamente dipendenti dal `Match`.
- Per la gestione dei deck abbiamo utilizzato un generic: in questo modo si evita duplicazione di codice, in quanto i mazzi hano tutti lo stesso funzionamento.
diff --git a/deliveries/PeerReview/PeerReview2-SequenceDiagram.md b/deliveries/Peer reviews/PeerReview2-SequenceDiagram.md
similarity index 100%
rename from deliveries/PeerReview/PeerReview2-SequenceDiagram.md
rename to deliveries/Peer reviews/PeerReview2-SequenceDiagram.md
diff --git a/deliveries/PeerReview/UML_peer_review.pdf b/deliveries/PeerReview/UML_peer_review.pdf
deleted file mode 100644
index 2ec6289e..00000000
Binary files a/deliveries/PeerReview/UML_peer_review.pdf and /dev/null differ
diff --git a/deliveries/Protocol/RMI_protocol.md b/deliveries/Protocol/RMI_protocol.md
index 2943914f..ca7a7250 100644
--- a/deliveries/Protocol/RMI_protocol.md
+++ b/deliveries/Protocol/RMI_protocol.md
@@ -5,7 +5,7 @@ This document is intended to describe how a client must be implemented and behav
- [General notes](#general-notes)
- [Obtain remote server object](#obtain-remote-server-object)
- [Obtain remote controller object](#obtain-remote-controller-object)
-- [Implement a commont interface](#implement-a-common-interface)
+- [Implement a common interface](#implement-a-common-interface)
## Content
@@ -13,11 +13,11 @@ This document is intended to describe how a client must be implemented and behav
- Keep in mind that this application has been designed and implemented with a distributed MVC architecture in mind, in particular having the view as the
only remote part of the architecture. It goes without saying that a client hereby is considered to be a view too.
- As usual on a network communication, the client must know the socket on which the server listens (i.e. IP and port).
-- It won't be described here how to access a RMI registry, since it's inherent to any RMI connection, not this specific protocol.
+- It won't be described here how to access an RMI registry, since it's inherent to any RMI connection, not this specific protocol.
### Obtain remote server object
The first step to be able to create a functioning ```Client``` class is to add to its ```main``` method everything needed to listen
-to the server remote RMI registry. The server will export itself to the registry as an object that implements the interface ```ServerRMIInterface```, then the received object must be casted to this interface:
+to the server remote RMI registry. The server will export itself to the registry as an object that implements the interface ```ServerRMIInterface```, then the received object must be cast to this interface:
```Java
/**
* RMI interface used to declare all and only the methods callable on a remote Server instance implementing this
@@ -27,38 +27,49 @@ to the server remote RMI registry. The server will export itself to the registry
*/
public interface ServerRMIInterface extends Remote {
/**
- * Returns the unique names of the available matches (those not full yet).
+ * Returns the available matches (those not full yet) as {@link AvailableMatch} instances.
+ *
* @return The list of Match which are not full yet.
* @throws RemoteException If the remote server is considered not to be reachable any more and cannot return as usual
*/
- List getJoinableMatches() throws RemoteException;
+ List getJoinableMatches() throws RemoteException;
/**
- * Lets the calling view join on a match with the given player nickname, if possible; gives back to the client
+ * Lets the calling view join on a match with the given player username, if possible; gives back to the client
* an instance of its PlayerControllerRMI, to start communicating through it with the match.
+ *
* @param matchName The unique name of the match to join to
- * @param nickname The chosen player nickname
+ * @param username The chosen player username
* @return An instance of PlayerControllerRMI, used exclusively by the calling view
- * @throws RemoteException If the remote server is considered not to be reachable any more and cannot return as usual
- * @throws ChosenMatchException If the chosen match is either already full or doesn't exist
- * @throws AlreadyUsedNicknameException If the given nickname is already taken
- * @throws WrongStateException If the match is in a state during which doesn't allow players to join any more
+ * @throws RemoteException If the remote server is considered not to be reachable any more and cannot return as usual
+ * @throws ChosenMatchException If the chosen match is either already full or doesn't exist
+ * @throws AlreadyUsedUsernameException If the given username is already taken
+ * @throws WrongStateException If the match is in a state during which doesn't allow players to join any more
+ * @throws WrongNameException If the name is not valid
*/
- PlayerControllerRMI joinMatch(String matchName, String nickname) throws RemoteException, ChosenMatchException, AlreadyUsedNicknameException, WrongStateException;
+ PlayerControllerRMIInterface joinMatch(String matchName, String username) throws RemoteException, ChosenMatchException, AlreadyUsedUsernameException, WrongStateException, WrongNameException;
/**
* Lets the calling view create a new match.
- * @param matchName The unique name to give to the new match
+ *
+ * @param matchName The unique name to give to the new match
* @param maxPlayers The maximum number of player allowed on the new match
- * @throws RemoteException If the remote server is considered not to be reachable any more and cannot return as usual
+ * @throws RemoteException If the remote server is considered not to be reachable any more and cannot return as usual
* @throws ChosenMatchException If the given match name is already taken
*/
- void createMatch(String matchName, int maxPlayers) throws RemoteException, ChosenMatchException;
+ void createMatch(String matchName, int maxPlayers) throws RemoteException, ChosenMatchException, WrongNameException;
+
+ /**
+ * Pings the server in order to perceive if the connection is still alive and working.
+ *
+ * @throws RemoteException If the connection to this class instance is not alive anymore
+ */
+ boolean ping() throws RemoteException;
}
```
### Obtain remote controller object
-Having received the ```ServerRMIInterface``` object allows the client to request a controller, it can be done calling the ```joinMatch(...)``` method. The received object is obviously remote too and must be casted to a common RMI interface (as usual with RMI): the ```PlayerControllerRMIInterface```:
+Having received the ```ServerRMIInterface``` object allows the client to request a controller, it can be done calling the ```joinMatch(...)``` method. The received object is obviously remote too and must be cast to a common RMI interface (as usual with RMI): the ```PlayerControllerRMIInterface```:
```Java
/**
* RMI interface used to declare all and only the methods callable on a remote PlayerControllerRMI instance implementing
@@ -72,61 +83,83 @@ public interface PlayerControllerRMIInterface extends Remote {
* called, it won't do anything, since it's call is allowed once per PlayerController object.
* It's used by a remote View having this class object to send itself over RMI to the PlayerControllerRMI
* instance.
+ *
* @param view The View to save in the PlayerController internal state
*/
- void registerView(ViewRMIInterface view) throws RemoteException;
+ void registerView(RemoteViewInterface view) throws RemoteException, ChosenMatchException, WrongStateException, AlreadyUsedUsernameException, WrongNameException;
/**
* Draws an initial card for the player.
+ *
* @throws WrongStateException If the current match state doesn't allow drawing an initial card
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongTurnException If the current turn it's not the one of this player
*/
void drawInitialCard() throws RemoteException, WrongStateException, WrongTurnException;
/**
* Communicates the chosen initial card side.
+ *
* @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
* @throws WrongStateException If the current match state doesn't allow setting the initial card side
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongTurnException If the current turn it's not the one of this player
*/
void chooseInitialCardSide(Side side) throws RemoteException, WrongStateException, WrongTurnException;
/**
* Draws two secret objectives.
+ *
* @throws WrongStateException If the current match state doesn't allow drawing secret objectives
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongTurnException If the current turn it's not the one of this player
*/
void drawSecretObjectives() throws RemoteException, WrongStateException, WrongTurnException;
/**
* Communicates the chosen secret objective.
+ *
* @param objective The chosen objective
- * @throws WrongStateException If the current match state doesn't allow choosing a secret objective
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongStateException If the current match state doesn't allow choosing a secret objective
+ * @throws WrongTurnException If the current turn it's not the one of this player
* @throws WrongChoiceException If the chosen objective is not one of the two drawn ones using {@link #drawSecretObjectives()}
*/
- void chooseSecretObjective(Objective objective) throweffectivelys RemoteException, WrongStateException, WrongTurnException, WrongChoiceException;
+ void chooseSecretObjective(Objective objective) throws RemoteException, WrongStateException, WrongTurnException, WrongChoiceException;
/**
* Plays a card.
+ *
* @param coords The coordinates on which to place the card
- * @param card The PlayableCard to play
- * @param side The side on which to play the chosen card
- * @throws WrongStateException If the current match state doesn't allow playing cards
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ * @throws WrongStateException If the current match state doesn't allow playing cards
+ * @throws WrongTurnException If the current turn it's not the one of this player
* @throws WrongChoiceException If the chosen card is not one of those in the player's current hand
*/
void playCard(Pair coords, PlayableCard card, Side side) throws RemoteException, WrongStateException, WrongTurnException, WrongChoiceException;
/**
* Draws a card.
+ *
* @param source The drawing source to draw the card from
- * @throws HandException If the player already has a full hand of cards (three cards)
- * @throws WrongStateException If the current match state doesn't allow drawing cards
- * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws HandException If the player already has a full hand of cards (three cards)
+ * @throws WrongStateException If the current match state doesn't allow drawing cards
+ * @throws WrongTurnException If the current turn it's not the one of this player
* @throws WrongChoiceException If the chosen DrawSource doesn't have any card left (i.e. it's empty)
*/
void drawCard(DrawSource source) throws RemoteException, HandException, WrongStateException, WrongTurnException, WrongChoiceException;
+
+ /**
+ * Sends a text to all the players in the match.
+ *
+ * @param text The text to be sent
+ */
+ void sendBroadcastText(String text) throws RemoteException;
+
+ /**
+ * Sends a text just to a specific player in the match.
+ *
+ * @param recipient The username of the recipient
+ * @param text The text to be sent to the recipient
+ */
+ void sendPrivateText(String recipient, String text) throws RemoteException;
}
```
This object from now on will be the one used to communicate with the match, which is effectively located on the server machine, thus the
@@ -140,36 +173,66 @@ To let the controller call methods on the client, the client must also implement
This interface is ```ViewRMIInterface```:
```Java
/**
- * RMI interface used to declare all and only the methods callable on a remote view instance implementing this interface.
+ * Network interface used to declare all and only the methods callable on a remote view instance implementing this interface or
+ * by message listener for TCP.
* Since it's a remote interface, all the methods here defined are meant to notify the occurrence of an event to the remote
* object. Given this, all methods also contain some parameters specific to the happened event.
* For security reasons, each method doesn't expose to the receiving view important objects (e.g. Player), but
- * rather values representing them (e.g. Player's nickname).
+ * rather values representing them (e.g. Player's username).
*/
-public interface ViewRMIInterface extends Remote {
+public interface RemoteViewInterface extends Remote {
+
/**
* Notifies that the match has just started.
* Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
* state.
- * @param playersNicknamesAndPawns Map that matches each pawn color to the corresponding player's nickname
- * @param playersHands Map that matches each player's nickname to the corresponding List of cards in the hand
- * @param visibleObjectives Pair of objectives visible to all players
- * @param visiblePlayableCards Map having as values the visible common cards (the keys are just indexes).
- * @param decksTopReigns Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
- * the first one is the gold deck one, the second one the resource deck one
- * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ *
+ * @param playersUsernamesAndPawns Map that matches each pawn color to the corresponding player's username
+ * @param playersHands Map that matches each player's username to the corresponding List of cards in the hand
+ * @param visibleObjectives Pair of objectives visible to all players
+ * @param visiblePlayableCards Map having as values the visible common cards (the keys are just indexes).
+ * @param decksTopReigns Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ * the first one is the gold deck one, the second one the resource deck one
+ * @throws RemoteException If the remote object is considered not to be reachable anymore and cannot return as usual
*/
- void matchStarted(Map playersNicknamesAndPawns, Map> playersHands, Pair visibleObjectives, Map visiblePlayableCards, Pair decksTopReigns) throws RemoteException;
+ void matchStarted(Map playersUsernamesAndPawns, Map> playersHands, Pair visibleObjectives, Map visiblePlayableCards, Pair decksTopReigns) throws RemoteException;
/**
- * Gives to the remote object an initial card to show it in the view.
- * @param initialCard Initial card to give
- * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ * Notifies that the match has resumed.
+ * Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ * state.
+ *
+ * @param playersUsernamesAndPawns Map that matches each pawn color to the corresponding player's username
+ * @param playersHands Map that matches each player's username to the corresponding List of cards in the hand
+ * @param visibleObjectives Pair of objectives visible to all players
+ * @param visiblePlayableCards Map having as values the visible common cards (the keys are just indexes).
+ * @param decksTopReigns Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ * the first one is the gold deck one, the second one the resource deck one
+ * @param secretObjective Secret objective of the current player
+ * @param availableResources Available resources of all the players
+ * @param placedCards Placed cards of all the players
+ * @param playerPoints Points of all the players
+ * @param currentPlayer The current player
+ * @param drawPhase If the match is resumed in draw phase
+ * @throws RemoteException If the remote object is considered not to be reachable anymore and cannot return as usual
+ */
+ void matchResumed(Map playersUsernamesAndPawns, Map> playersHands,
+ Pair visibleObjectives, Map visiblePlayableCards,
+ Pair decksTopReigns, Objective secretObjective, Map> availableResources,
+ Map, PlacedCard>> placedCards, Map playerPoints, String currentPlayer, boolean drawPhase) throws RemoteException;
+
+ /**
+ * Gives the graphical view a list of available matches
+ *
+ * @param availableMatchs The available matches
*/
+ void receiveAvailableMatches(List availableMatchs) throws RemoteException;
+
void giveInitialCard(InitialCard initialCard) throws RemoteException;
/**
* Gives to the remote object a pair of secret objectives to show them in the view and to choose one from them.
+ *
* @param secretObjectives Pair of secret objectives to give
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
@@ -177,61 +240,104 @@ public interface ViewRMIInterface extends Remote {
/**
* Notifies that someone (it may or may not be the receiving View instance) has drawn its initial card.
- * @param someoneNickname The nickname of the player who has drawn the card
- * @param card The card drawn
+ *
+ * @param someoneUsername The username of the player who has drawn the card
+ * @param card The card drawn
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someoneDrewInitialCard(String someoneNickname, InitialCard card) throws RemoteException;
+ void someoneDrewInitialCard(String someoneUsername, InitialCard card) throws RemoteException;
/**
* Notifies that someone (it may or may not be the receiving View instance) has decided (then set) its initial card side.
- * @param someoneNickname The nickname of the player who has set side
- * @param side The chosen side
+ *
+ * @param someoneUsername The username of the player who has set side
+ * @param side The chosen side
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someoneSetInitialSide(String someoneNickname, Side side) throws RemoteException;
+ void someoneSetInitialSide(String someoneUsername, Side side, Map availableResources) throws RemoteException;
/**
* Notifies that someone (it may or may not be the receiving View instance) has drawn a pair of secret objectives.
* Mind that the objectives are not passed as arguments, since they are secret to all players but the one receiving
* them. The one meant to receive them receives this message too but obtain the objectives through the
* giveSecretObjective() method.
- * @param someoneNickname The nickname of the player who has drawn the card
+ *
+ * @param someoneUsername The username of the player who has drawn the card
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someoneDrewSecretObjective(String someoneNickname) throws RemoteException;
+ void someoneDrewSecretObjective(String someoneUsername) throws RemoteException;
/**
* Notifies that someone (it may or may not be the receiving View instance) has chosen theirs secret objective.
- * @param someoneNickname The nickname of the player who has chosen theirs secret objective
+ *
+ * @param someoneUsername The username of the player who has chosen theirs secret objective
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someoneChoseSecretObjective(String someoneNickname) throws RemoteException;
+ void someoneChoseSecretObjective(String someoneUsername) throws RemoteException;
/**
* Notifies that someone (it may or may not be the receiving View instance) has played a card.
- * @param someoneNickname The nickname of the player who has played a card
- * @param coords The coordinates where the card has been placed as a Pair of int
- * @param card The card that has been played
- * @param side The side on which the card has been played
+ *
+ * @param someoneUsername The username of the player who has played a card
+ * @param coords The coordinates where the card has been placed as a Pair of int
+ * @param card The card that has been played
+ * @param side The side on which the card has been played
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someonePlayedCard(String someoneNickname, Pair coords, PlayableCard card, Side side) throws RemoteException;
+ void someonePlayedCard(String someoneUsername, Pair coords, PlayableCard card, Side side, int points, Map availableResources) throws RemoteException;
/**
* Notifies that someone (it may or may not be the receiving View instance) has drawn a card.
- * @param someoneNickname The nickname of the player who has played a card
- * @param source The DrawSource from which the card has been drawn
- * @param card The card that has been drawn
+ *
+ * @param someoneUsername The username of the player who has played a card
+ * @param source The DrawSource from which the card has been drawn
+ * @param card The card that has been drawn
+ * @param replacementCard The card that replaced the drawn one
+ * @param deckTopReigns The decks top reigns
+ * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ */
+ void someoneDrewCard(String someoneUsername, DrawSource source, PlayableCard card, PlayableCard replacementCard, Pair deckTopReigns) throws RemoteException;
+
+ /**
+ * Notifies that someone has joined the match.
+ *
+ * @param someoneUsername The username of the player that joined
+ * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ */
+ void someoneJoined(String someoneUsername, List joinedPlayers) throws RemoteException;
+
+ /**
+ * Notifies that someone has quit the match.
+ *
+ * @param someoneUsername The username of the player that quit
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void someoneDrewCard(String someoneNickname, DrawSource source, Card card) throws RemoteException;
+ void someoneQuit(String someoneUsername) throws RemoteException;
/**
* Notifies that the match has just started.
+ *
+ * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ */
+ void matchFinished(List ranking) throws RemoteException;
+
+ /**
+ * Notifies that a new message in the global chat has been received.
+ *
+ * @param someoneUsername Username of the user that sent the message
+ * @param text Content of the message
+ * @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
+ */
+ void someoneSentBroadcastText(String someoneUsername, String text) throws RemoteException;
+
+ /**
+ * Notifies that a new private message has been sent to the current user.
+ *
+ * @param someoneUsername Username of the user that sent the message
+ * @param text Content of the message
* @throws RemoteException If the remote object is considered not to be reachable any more and cannot return as usual
*/
- void matchFinished() throws RemoteException;
+ void someoneSentPrivateText(String someoneUsername, String text) throws RemoteException;
}
```
-Each method must be implemented keeping in mind that it represent the action to be triggered when the corresponding event occurs.
\ No newline at end of file
+Each method must be implemented keeping in mind that it represent the action to be triggered when the corresponding event occurs.
diff --git a/deliveries/Protocol/TCP_protocol.md b/deliveries/Protocol/TCP_protocol.md
index b83e946f..94ede312 100644
--- a/deliveries/Protocol/TCP_protocol.md
+++ b/deliveries/Protocol/TCP_protocol.md
@@ -17,14 +17,14 @@ Implemented actions:
- [GetAvailableMatches](#GetAvailableMatches)
- [CreateMatch](#CreateMatch)
- [JoinMatch](#JoinMatch)
-- [SendBroadcastText](#SendBroadcastText)
-- [SendPrivateText](#SendText)
- [DrawInitialCard](#DrawInitialCard)
- [ChooseInitialCardSide](#ChooseInitialCardSide)
- [DrawSecretObjectives](#DrawSecretObjectives)
- [ChooseSecretObjective](#ChooseSecretObjective)
- [PlayCard](#PlayCard)
- [DrawCard](#DrawCard)
+- [SendBroadcastText](#SendBroadcastText)
+- [SendPrivateText](#SendPrivateText)
### GetAvailableMatches
The action does not need additional parameters.
@@ -40,27 +40,12 @@ The action communicates (to the server) the intention of a client to create a ne
| `maxPlayers` | Integer | Number of maximum players (must be between 2 and 4) |
### JoinMatch
-The action communicates the intention of a client to join a match.
+The action communicates the intention of a client to join (or reconnect to) a match.
| Parameter | Type | Description |
| :-------- | :----: | :------------------------ |
| `matchName` | String | Name of the match to join |
-### SendBroadcastText
-The action sends a public text message in the chat.
-
-| Parameter | Type | Description |
-| :-------- | :-----------------: | :-------------------------------------------------------------------------------- |
-| `text` | String | Content of the message |
-
-### SendPrivateText
-The action sends a private text message in the chat to the specified recipient.
-
-| Parameter | Type | Description |
-| :-------- | :-----------------: | :-------------------------------------------------------------------------------- |
-| `text` | String | Content of the message |
-| `recipient` | String | Recipient's name of the private message. |
-
### DrawInitialCard
The action does not need additional parameters.
- It communicates the intention of a player to draw the initial card. It can only happen before the first turn of the game.
@@ -105,13 +90,31 @@ If the action is successful, a [SomeonePlayedCard](#SomeonePlayedCard) response
### DrawCard
The action communicates the intention of a player to draw a card. It can only happen during the player's own turn.
-
| Parameter | Type | Description |
| :--------- | :----: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `drawSource` | String | Source from which drawing the card. It can be `FIRST_VISIBLE_CARD`, `SECOND_VISIBLE_CARD`, `THIRD_VISIBLE_CARD`, `FOURTH_VISIBLE_CARD`, `GOLDS_DECK`, `RESOURCES_DECK` |
If the action is successful, a [SomeoneDrewInitialCard](#SomeoneDrewInitialCard) response is sent to every client.
+### SendBroadcastText
+The action communicates the intention of the player to write a text in the chat, visible to every member of the game
+
+| Parameter | Type | Description |
+| :-------- | :-----: | :---------------------------------------------- |
+| `text` | String | The content of the text |
+
+If the action is successful, a [SomeoneSentBroadcastText](#SomeoneSentBroadcastText) response is sent to every client.
+
+### SendPrivateText
+The action communicates the intention of the player to write a private text, visible only to himself and the player specified
+
+| Parameter | Type | Description |
+| :-------- | :-----: | :---------------------------------------------- |
+| `recipient` | String | The username of the recipient |
+| `text` | String | The content of the text |
+
+If the action is successful, a [SomeoneSentPrivateText](#SomeoneSentPrivateText) response is sent to the current and the specified player
+
## Responses
Responses always have the following parameter:
@@ -123,16 +126,18 @@ Response can either be:
- [AvailableMatches](#AvailableMatches)
- [SomeoneJoined](#SomeoneJoined)
- [SomeoneQuit](#SomeoneQuit)
-- [SomeoneSentPrivateText](#SomeoneSentPrivateText)
-- [SomeoneSentBroadcastText](#SomeoneSentBroadcastText)
- [MatchStarted](#MatchStarted)
- [SomeoneDrewInitialCard](#SomeoneDrewInitialCard)
- [SomeoneSetInitialSide](#SomeoneSetInitialSide)
- [SomeoneDrewSecretObjectives](#SomeoneDrewSecretObjectives)
- [SomeoneChoseSecretObjective](#SomeoneChoseSecretObjective)
-- [SomeonePlayedCardMessage](#SomeonePlayedCardMessage)
+- [SomeonePlayedCard](#SomeonePlayedCard)
- [SomeoneDrewCard](#SomeoneDrewCard)
- [MatchFinishedMessage](#MatchFinishedMessage)
+- [SomeoneSentBroadcastText](#SomeoneSentBroadcastText)
+- [SomeoneSentPrivateText](#SomeoneSentPrivateText)
+- [MatchResumed](#MatchResumed)
+
### AvailableMatches
This response is sent when a user is connected to the server.
@@ -167,23 +172,6 @@ This response is sent when a player quits the current match.
| `joinedPlayers` | Integer | Number of players that currently joined the match |
| `endMatch` | bool | true if the quit caused the match to interrupt, false otherwise|
-### SomeoneSentBroadcastText
-This response is sent when another player sends a message in the chat.
-
-| Parameter | Type | Description |
-| :-------- | :----: | :----------------------------------------------------- |
-| `username` | String | Username of the player that sent the message |
-| `text` | String | Text of the message sent |
-
-### SomeoneSentPrivateText
-This response is sent when another player sends a private message to another player.
-
-| Parameter | Type | Description |
-| :-------- | :----: | :----------------------------------------------------- |
-| `username` | String | Username of the player that sent the message |
-| `recipient` | String | Username of the player that sent the message |
-| `text` | String | Text of the message sent |
-
### MatchStarted
Sent when the required amount of players is reached and the match is about to start.
@@ -311,11 +299,11 @@ This response is sent to each user in the match when a user plays a card.
### Rank
Rank is a JSON object containing the results of a single player.
-| Parameter | Type | Description |
-| :-------- | :-----: | :----------------------------- |
-| `username` | String | Username of the current player |
-| `points` | Integer | Amount of the final points (after post-match objective counting) |
-| `winner` | bool | If the current player is also the winner of the game |
+| Parameter | Type | Description |
+| :-------- | :-----: | :-------------------------------------------------------------------|
+| `username` | String | Username of the current player |
+| `points` | Integer | Amount of the final points (after post-match objective counting) |
+| `winner` | bool | If the current player is also the winner of the game |
#### Example
```JSON
@@ -335,6 +323,38 @@ Rank is a JSON object containing the results of a single player.
"response": "MatchFinished"
}
```
+### SomeoneSentBroadcastText
+This response is sent to each user in the match when a user sends a text in the chat.
+
+| Parameter | Type | Description |
+| :------------------- | :----------------: | :------------------------------------------------ |
+| `text` | String | The content of the text |
+
+### SomeoneSentPrivateText
+This response is sent to the user who sent the message and to the recipient.
+
+| Parameter | Type | Description |
+| :------------------- | :----------------: | :------------------------------------------------ |
+| `recipient` | String | The recipient of the private text |
+| `text` | String | The content of the text |
+
+### MatchResumed
+This response is sent to the user who just rejoined a match. All the parameters refer to the status of the match before the server crashed
+
+| Parameter | Type | Description |
+| :------------------------ | :-------------------------------------------: | :------------------------------------------------------ |
+| `playersUsernamesAndPawns` | Map | Map from players' username to pawn color |
+| `playersHands` | Map> | Map from players' username to their hand |
+| `visibleObjectives` | Pair | The two visible objectives common to every player |
+| `visiblePlayableCards` | Map | The four drawable cards visible to everyone |
+| `decksTopReigns` | Pair | The reign of the two decks (resource and gold) |
+| `secretObjective` | Integer | The secret objective ID of the player |
+| `availableResources` | Map> | Map from players' username to their available resources |
+| `placedCards` | Map> | Map from players' username to their board |
+| `playerPoints` | Map | Map from players' username to their points |
+| `currentPlayer` | String | Username of the player currently playing his turn |
+| `drawPhase` | boolean | Whether the current player should play or draw a card |
+
## Errors
An error always contains these parameters:
diff --git a/deliveries/README.md b/deliveries/README.md
index 030f1b89..e5ef3fb2 100644
--- a/deliveries/README.md
+++ b/deliveries/README.md
@@ -1,6 +1,7 @@
# Cosa troviamo qui dentro?
-Si trova:
-- UML iniziale e finale
-- Documenti
-- Documenti di peer review
-- Report su test results
+
+- Diagrammi UML del progetto (Cartella UML)
+- Documentazione dei protocolli TCP e RMI (Cartella Protocol)
+- Peer Review inviate da noi (Cartella PeerReview)
+- JavaDoc del progetto (Cartella JavaDoc)
+- File JAR eseguibili (Cartella JAR)
diff --git a/deliveries/Test results/index.html b/deliveries/Test results/index.html
new file mode 100644
index 00000000..bf13119f
--- /dev/null
+++ b/deliveries/Test results/index.html
@@ -0,0 +1 @@
+AM08
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.html
new file mode 100644
index 00000000..7528e99a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.html
@@ -0,0 +1 @@
+ChatPaneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.java.html
new file mode 100644
index 00000000..2628a512
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ChatPaneController.java.html
@@ -0,0 +1,210 @@
+ChatPaneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import javafx.animation.TranslateTransition;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.KeyCode;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+import javafx.scene.text.TextFlow;
+import javafx.util.Duration;
+
+import java.util.*;
+
+/**
+ * JavaFX controller for the chat pane
+ */
+public class ChatPaneController extends SceneController {
+ @FXML
+ private Button sendMessageBtn;
+ @FXML
+ private TextFlow chatHistoryText;
+ @FXML
+ private HBox chatPane;
+ @FXML
+ private Button showChatBtn;
+ @FXML
+ private TextField chatInputText;
+ @FXML
+ private ChoiceBox<String> chatSelector;
+
+ public ScrollPane chatHistoryScrollPane;
+ public VBox chatContainer;
+ private final Map<String, String> chatHistory = new HashMap<>();
+ private boolean isVisible;
+ private Queue<String> unconfirmedPrivateMessagesReceivers;
+
+ @Override
+ public void initialize() {
+ chatHistoryScrollPane.getStyleClass().clear();
+ isVisible = false;
+ chatPane.setTranslateX(460);
+ setIconStatus(false);
+
+ unconfirmedPrivateMessagesReceivers = new LinkedList<>();
+
+ // Add the broadcast item as an entry in the chatSelector and set it as default item
+ chatHistory.put("broadcast", "");
+ chatSelector.getItems().add("broadcast");
+ chatSelector.getSelectionModel().select("broadcast");
+
+ // Show/hide the chat pane when the outer button is clicked
+ showChatBtn.setOnMouseClicked((mouseEvent -> {
+ isVisible = !isVisible;
+ changeVisibility();
+ }));
+
+ // Send message written in input box on send message button clicked
+ sendMessageBtn.setOnMouseClicked(mouseEvent -> {
+ this.submitMessage();
+ });
+ chatInputText.setOnKeyPressed(keyEvent -> {
+ if (keyEvent.getCode() == KeyCode.ENTER) {
+ this.submitMessage();
+ }
+ });
+
+ // Switch to chat when a chatSelector item is clicked
+ chatSelector.setOnAction(actionEvent -> {
+ String selectedChatName = chatSelector.getSelectionModel().getSelectedItem();
+ String selectedChatHistory = chatHistory.get(selectedChatName);
+
+ chatHistoryText.getChildren().clear();
+ chatHistoryText.getChildren().add(new Text(selectedChatHistory));
+ });
+ }
+
+ /**
+ * Submit a message written in the chat. Calls the network and
+ * clears the input field
+ */
+ private void submitMessage() {
+ String inputText = chatInputText.getText();
+ String selectedChatName = chatSelector.getSelectionModel().getSelectedItem();
+
+ if (!inputText.isBlank()) {
+ if (selectedChatName.equals("broadcast")) {
+ super.view.sendBroadcastText(inputText);
+ } else {
+ super.view.sendPrivateText(selectedChatName, inputText);
+ unconfirmedPrivateMessagesReceivers.offer(selectedChatName);
+ }
+ }
+
+ chatInputText.clear();
+ }
+ /**
+ * Adds a player to the chat.
+ *
+ * @param playerUsername The player's username
+ */
+ public void addPlayer(String playerUsername) {
+ chatHistory.put(playerUsername, "");
+ chatSelector.getItems().add(playerUsername);
+ }
+
+ /**
+ * Handles the receipt of a broadcast message from a user who's not the current client.
+ * For this purpose the method confirmSubmitBroadcastMessage(...) can be used.
+ *
+ * @param senderUsername The username of the player that sent the message
+ * @param message The message sent
+ */
+ public void receiveBroadcastMessage(String senderUsername, String message) {
+ addMessage("broadcast", senderUsername, message);
+ }
+
+ /**
+ * Handles the receipt of a private message from a user who's not the current client.
+ * For this purpose the method confirmSubmitPrivateMessage(...) can be used.
+ *
+ * @param senderUsername The username of the player that sent the message
+ * @param message The message sent
+ */
+ public void receivePrivateMessage(String senderUsername, String message) {
+ addMessage(senderUsername, senderUsername, message);
+ }
+
+ /**
+ * Handles the receipt of confirmation of successful submit of a broadcast message
+ * from this client to the server (either broadcast or private).
+ *
+ * @param message The message to be confirmed
+ */
+ public void confirmSubmitBroadcastMessage(String message) {
+ addMessage("broadcast", super.view.getUsername(), message);
+ }
+
+ /**
+ * Handles the receipt of confirmation of successful submit of a private message
+ * from this client to the server (either broadcast or private).
+ *
+ * @param message The message to be confirmed
+ */
+ public void confirmSubmitPrivateMessage(String message) {
+ String receiverUsername = unconfirmedPrivateMessagesReceivers.poll();
+ addMessage(receiverUsername, super.view.getUsername(), message);
+ }
+
+ /**
+ * Utility method to add a message to the chat pane
+ *
+ * @param chatName The chat in which to put the message
+ * @param senderUsername The sender's username
+ * @param message Text message text content
+ */
+ private void addMessage(String chatName, String senderUsername, String message) {
+ String textMessage = senderUsername + ": " + message + "\n";
+
+ // Update the specific chat history associated to the sender
+ String updatedHistory = chatHistory.get(chatName) + textMessage;
+ chatHistory.put(chatName, updatedHistory);
+
+ // If it's the currently selected chat is this one
+ if (chatSelector.getSelectionModel().getSelectedItem().equals(chatName)) {
+ // Visually update the chat pane
+ chatHistoryText.getChildren().clear();
+ chatHistoryText.getChildren().add(new Text(updatedHistory));
+ }
+ }
+
+ /**
+ * Switches the chat pane from visible to not visible and viceversa.
+ */
+ private void changeVisibility() {
+ TranslateTransition tt = new TranslateTransition(Duration.millis(200), chatPane);
+ if (isVisible) {
+ tt.setToX(0);
+ setIconStatus(true);
+ } else {
+ tt.setToX(460);
+ setIconStatus(false);
+ }
+ tt.play();
+ }
+
+ /**
+ * Changes the icon of the button to open the chat.
+ *
+ * @param opened True if the chat is opened, false otherwise
+ */
+ private void setIconStatus(boolean opened) {
+ String path;
+ if (opened) {
+ path = "/images/icons/right.png";
+ } else {
+ path = "/images/icons/left.png";
+ }
+ ImageView img = new ImageView(new Image(path));
+ img.setFitHeight(35);
+ img.setFitWidth(35);
+ showChatBtn.setGraphic(img);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.html
new file mode 100644
index 00000000..664595f1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.html
@@ -0,0 +1 @@
+ConnectionSceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.java.html
new file mode 100644
index 00000000..acd4d2bc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ConnectionSceneController.java.html
@@ -0,0 +1,167 @@
+ConnectionSceneController.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.html
new file mode 100644
index 00000000..f36ec0f8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.html
@@ -0,0 +1 @@
+ErrorSceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.java.html
new file mode 100644
index 00000000..5e1e2b52
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/ErrorSceneController.java.html
@@ -0,0 +1,52 @@
+ErrorSceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+
+import it.polimi.ingsw.utils.GuiUtil;
+import javafx.fxml.FXML;
+import javafx.scene.text.Text;
+
+import java.io.IOException;
+
+/**
+ * Controller of the error scene window
+ */
+public class ErrorSceneController extends SceneController {
+ // Window dimensions
+ public static double windowWidth = 500;
+ public static double windowHeight = 200;
+ public Text errorTitle;
+
+ @FXML
+ private Text errorText;
+
+ @Override
+ public void initialize() throws IOException {
+
+ }
+
+ /**
+ * Set the text of the error that is shown in the bottom
+ * @param text text of the error
+ */
+ public void setText(String text) {
+ errorText.setText(text);
+ }
+
+ /**
+ * Set the title of the error that is shown on the top
+ * @param title text of the title
+ */
+ public void setTitle(String title) {
+ errorTitle.setText(title);
+ }
+
+ /**
+ * Show an error from an exception
+ * @param e exception
+ */
+ public void setErrror(Exception e) {
+ errorTitle.setText(GuiUtil.getExceptionTitle(e));
+ errorText.setText(e.getMessage());
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.html
new file mode 100644
index 00000000..e5a4a0e6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.html
@@ -0,0 +1 @@
+LobbySceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.java.html
new file mode 100644
index 00000000..180bc7c2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/LobbySceneController.java.html
@@ -0,0 +1,170 @@
+LobbySceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalApplication;
+import it.polimi.ingsw.exceptions.WrongInputFormatException;
+import it.polimi.ingsw.gamemodel.Color;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.GuiUtil;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.*;
+import javafx.scene.shape.Circle;
+
+import java.io.IOException;
+import java.util.List;
+
+public class LobbySceneController extends SceneController {
+ @FXML
+ public TextField createUsername;
+ @FXML
+ public TextField matchName;
+ @FXML
+ public HBox matchNumberContainer;
+ public Label joinTitle;
+ @FXML
+ VBox matchContainer;
+ @FXML
+ ScrollPane lobbyScrollPane;
+ @FXML
+ Button joinButton;
+ @FXML
+ TextField joinUsername;
+ @FXML
+ Button createButton;
+ ToggleGroup matchJoinToggle;
+ ToggleGroup matchNumberToggle;
+
+ public void initialize() {
+ matchJoinToggle = new ToggleGroup();
+ matchNumberToggle = new ToggleGroup();
+ lobbyScrollPane.getStyleClass().clear();
+ }
+
+ @Override
+ public void initializePostController() {
+ joinTitle.setOnMouseClicked((event) -> refreshMatches());
+ createButton.setOnMouseClicked((e) -> {
+ RadioButton radioButton = (RadioButton) matchNumberToggle.getSelectedToggle();
+ if (radioButton.getText().isEmpty() || createUsername.getText().isEmpty() || matchName.getText().isEmpty() ) {
+ view.notifyError(new WrongInputFormatException("Username or match name or max players not chosen"));
+ return;
+ }
+ view.setUsername(createUsername.getText());
+ view.createMatch(matchName.getText(), Integer.valueOf(radioButton.getText()));
+
+ });
+ joinButton.setOnMouseClicked((e) -> {
+ view.setUsername(joinUsername.getText());
+ RadioButton radiobutton = (RadioButton) matchJoinToggle.getSelectedToggle();
+ if (radiobutton == null || joinUsername.getText().isEmpty() ) {
+ view.notifyError(new WrongInputFormatException("Username or match not chosen"));
+ return;
+ }
+ view.joinMatch((String) radiobutton.getProperties().get("matchName"));
+ });
+ matchNumberContainer.getChildren().forEach((button) -> {((RadioButton) button).setToggleGroup(matchNumberToggle);});
+ }
+
+ /**
+ * Show the wait scene
+ * @return the wait scene controller
+ * @throws IOException in case of file error
+ */
+ public WaitingSceneController showWaitScene() throws IOException {
+ StackPane root = loadScene("/fxml/waiting.fxml");
+ GuiUtil.applyCSS(root, "/css/match.css");
+ WaitingSceneController controller = (WaitingSceneController) root.getProperties().get("Controller");
+ Scene matchScene = new Scene(root, GraphicalApplication.screenWidth, GraphicalApplication.screenHeight);
+ stage.setScene(matchScene);
+ return controller;
+ }
+
+ private void refreshMatches() {
+ matchContainer.getChildren().clear();
+ matchContainer.getChildren().add(new ProgressIndicator());
+ view.getAvailableMatches();
+ }
+
+ /**
+ * Set the matches displayed
+ * @param matchList List of the current available matches
+ */
+ public void updateMatches(List<AvailableMatch> matchList) {
+ matchContainer.getChildren().clear();
+ if (matchList.isEmpty()) {
+ Label emptyLabel = new Label("There is no match");
+ emptyLabel.getStyleClass().add("input-label");
+ matchContainer.getChildren().add(emptyLabel);
+ }
+ for (AvailableMatch m : matchList) {
+ addMatchCard(m.name(), m.currentPlayers(), m.maxPlayers(), m.isRejoinable());
+ }
+ }
+
+ /**
+ * Create the container for a Match
+ * @param name name of the match
+ * @param players current amount of players
+ * @param maxPlayers maximum number of players allowed in the match
+ * @param isRejoinable if the match is rejoinable
+ */
+ public void addMatchCard(String name, int players, int maxPlayers, boolean isRejoinable) {
+ Color c;
+ if (isRejoinable) {
+ c = Color.YELLOW;
+ } else if (maxPlayers == players) {
+ c = Color.RED;
+ } else {
+ c = Color.GREEN;
+ }
+
+
+ HBox matchCard = new HBox();
+ matchCard.getStyleClass().add("lobby-card");
+ matchCard.getStyleClass().add("lobby-card-" + matchContainer.getChildren().size()%2);
+ matchCard.setAlignment(Pos.CENTER);
+
+ RadioButton button = new RadioButton();
+ button.setDisable(players == maxPlayers && !isRejoinable);
+ button.setAlignment(Pos.CENTER);
+ button.getStyleClass().add("radio");
+ button.getStyleClass().add("lobby-radio");
+ button.setToggleGroup(this.matchJoinToggle);
+ button.getProperties().put("matchName", name);
+ //button.setText(name);
+ matchCard.getChildren().add(button);
+
+ Label nameLabel = new Label(name);
+ nameLabel.setAlignment(Pos.CENTER);
+ nameLabel.getStyleClass().add("lobby-title");
+ matchCard.getChildren().add(nameLabel);
+
+ Pane spacer = new Pane();
+ HBox.setHgrow(spacer, Priority.ALWAYS);
+ matchCard.getChildren().add(spacer);
+
+
+ // Match status indicator
+ Circle status = new Circle();
+ spacer = new Pane();
+ spacer.setPrefWidth(3);
+ status.setFill(javafx.scene.paint.Color.web(GuiUtil.getHexFromColor(c)));
+ status.setRadius(16);
+ // Players label
+ Label playerLabel = new Label(players + "/" + maxPlayers);
+ playerLabel.setAlignment(Pos.CENTER);
+ playerLabel.getStyleClass().add("lobby-title");
+ matchCard.getChildren().add(playerLabel);
+ matchCard.getChildren().add(spacer);
+ matchCard.getChildren().add(status);
+
+ Pane spacer2 = new Pane();
+ spacer2.setPrefWidth(5);
+ matchCard.getChildren().add(spacer2);
+
+ matchContainer.getChildren().add(matchCard);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.html
new file mode 100644
index 00000000..c2886595
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.html
@@ -0,0 +1 @@
+MatchSceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.java.html
new file mode 100644
index 00000000..bb97f1bc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/MatchSceneController.java.html
@@ -0,0 +1,195 @@
+MatchSceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalApplication;
+import it.polimi.ingsw.client.frontend.gui.nodes.CardView;
+
+import it.polimi.ingsw.client.frontend.gui.nodes.PlateauPane;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.GuiUtil;
+import it.polimi.ingsw.utils.Pair;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Cursor;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JavaFX controller of the match scene
+ */
+public class MatchSceneController extends SceneController {
+ public Tab tableTab;
+ public Label stateTitle;
+ @FXML
+ TabPane matchTabs;
+ @FXML
+ AnchorPane matchPane;
+
+ public CardView goldsDeck;
+ public CardView resourcesDeck;
+ public CardView firstVisible;
+ public CardView secondVisible;
+ public CardView thirdVisible;
+ public CardView fourthVisible;
+ public CardView firstObjective;
+ public CardView secondObjective;
+ public PlateauPane plateauPane;
+ public HBox chatPane;
+ Map<String, Tab> tabs = new HashMap<>();
+
+ public void initialize() {
+ }
+
+ @Override
+ public void initializePostController() throws IOException {
+ goldsDeck.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.GOLDS_DECK));
+ goldsDeck.setCursor(Cursor.HAND);
+ resourcesDeck.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.RESOURCES_DECK));
+ resourcesDeck.setCursor(Cursor.HAND);
+ firstVisible.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.FIRST_VISIBLE));
+ firstVisible.setCursor(Cursor.HAND);
+ secondVisible.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.SECOND_VISIBLE));
+ secondVisible.setCursor(Cursor.HAND);
+ thirdVisible.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.THIRD_VISIBLE));
+ thirdVisible.setCursor(Cursor.HAND);
+ fourthVisible.setOnMouseClicked((clickEvent) -> view.drawCard(DrawSource.FOURTH_VISIBLE));
+ fourthVisible.setCursor(Cursor.HAND);
+
+ // Load the chat pane
+ chatPane = loadScene("/fxml/chat.fxml");
+ matchPane.getChildren().add(chatPane);
+ }
+
+ /**
+ * Add the tab of the given player
+ * @param username username of the player
+ * @param color color of the player
+ * @return controller of the created player tab
+ * @throws IOException if there are file errors
+ */
+ public PlayerTabController addPlayerTab(String username, Color color) throws IOException {
+ // Load the tab
+ FXMLLoader loader = GuiUtil.getLoader("/fxml/player_tab.fxml");
+ Tab t = loader.load();
+ setControllerAttributes(loader);
+ // Add the tab
+ t.setText(username);
+ Circle icon = new Circle();
+ icon.setRadius(15);
+ icon.setFill(javafx.scene.paint.Color.web(GuiUtil.getHexFromColor(color)));
+ t.setGraphic(icon);
+ matchTabs.getTabs().add(t);
+ // Add properties to the controller
+ PlayerTabController controller = loader.getController();
+ controller.setUsername(username);
+ t.getProperties().put("Controller", controller);
+ // Add colored pawn
+ plateauPane.setColor(username, color);
+ tabs.put(username, t);
+ return controller;
+ }
+
+ /**
+ * Set the displayed card for the given draw source
+ * @param source Source of the draw
+ * @param replacementCard Replacement card, can be null
+ * @param replacementReign Replacement reign, can be null
+ */
+ public void setDrawSource (DrawSource source, PlayableCard replacementCard, Symbol replacementReign) {
+ switch (source) {
+ case DrawSource.GOLDS_DECK -> goldsDeck.setGoldsCardBack(replacementReign);
+ case DrawSource.RESOURCES_DECK -> resourcesDeck.setResourcesCardBack(replacementReign);
+ case DrawSource.FIRST_VISIBLE -> firstVisible.setCard(replacementCard, Side.FRONT);
+ case DrawSource.SECOND_VISIBLE -> secondVisible.setCard(replacementCard, Side.FRONT);
+ case DrawSource.THIRD_VISIBLE -> thirdVisible.setCard(replacementCard, Side.FRONT);
+ case DrawSource.FOURTH_VISIBLE -> fourthVisible.setCard(replacementCard, Side.FRONT);
+ }
+ }
+
+ /**
+ * Set the visible objectives cards
+ * @param objectives pair of the objectives
+ */
+ public void setObjectives(Pair<Objective, Objective> objectives) {
+ firstObjective.setCard(objectives.first(), Side.FRONT);
+ secondObjective.setCard(objectives.second(), Side.FRONT);
+ }
+
+ /**
+ * Move the player pawn on the plateau
+ * @param username username of the player to move
+ * @param points total amount of points of the player
+ */
+ public void setPlateauPoints(String username, int points) {
+ this.plateauPane.setPoints(username, points);
+ }
+
+ /**
+ * Show the ranking scene on match end
+ * @return ranking scene controller
+ * @throws IOException on file errors
+ */
+ public RankingSceneController showRankingScene() throws IOException {
+ StackPane root = loadScene("/fxml/ranking.fxml");
+ Scene rankingScene = new Scene(root, GraphicalApplication.screenWidth, GraphicalApplication.screenHeight);
+ GuiUtil.applyCSS(root, "/css/style.css");
+ stage.setScene(rankingScene);
+ return (RankingSceneController) root.getProperties().get("Controller");
+ }
+
+ /**
+ * Get the chat pane controller
+ * @return the chat pane controller
+ */
+ public ChatPaneController getChatPane() {
+ return (ChatPaneController) chatPane.getProperties().get("Controller");
+ }
+
+ /**
+ * Force the focus on a player's tab
+ * @param username username of the player
+ */
+ public void setFocus(String username) {
+ matchTabs.getSelectionModel().select(tabs.get(username));
+ }
+
+ /**
+ *
+ */
+ public void setFocusToTable() {
+ matchTabs.getSelectionModel().select(tableTab);
+ }
+
+ /**
+ * Enables/disables mouse interactions with draw sources.
+ *
+ * @param enable True if interactions should be enabled, false otherwise
+ */
+ public void enableDrawSourcesInteractions(boolean enable) {
+ goldsDeck.setDisable(!enable);
+ resourcesDeck.setDisable(!enable);
+ firstVisible.setDisable(!enable);
+ secondVisible.setDisable(!enable);
+ thirdVisible.setDisable(!enable);
+ fourthVisible.setDisable(!enable);
+ }
+
+ /**
+ * Set the current state title to express an action while playing
+ * @param text title
+ */
+ public void setStateTitle(String text) {
+ stateTitle.setText(text);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.html
new file mode 100644
index 00000000..d5403f73
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.html
@@ -0,0 +1 @@
+PlayerTabController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.java.html
new file mode 100644
index 00000000..5e5d6f08
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/PlayerTabController.java.html
@@ -0,0 +1,401 @@
+PlayerTabController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.nodes.BoardPane;
+import it.polimi.ingsw.client.frontend.gui.nodes.CardView;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.CardsManager;
+import it.polimi.ingsw.utils.Pair;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Cursor;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.Tab;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.ClipboardContent;
+import javafx.scene.input.Dragboard;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.TransferMode;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.StackPane;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Controller of the player tab
+ */
+public class PlayerTabController extends SceneController {
+ public HBox handCards;
+ @FXML
+ private StackPane rootPane;
+ @FXML
+ private Tab playerTab;
+ private String username;
+ @FXML
+ BoardPane playerBoard;
+ @FXML
+ ScrollPane scroll;
+ @FXML
+ HBox resourcesCounter;
+ @FXML
+ Label pointsCounter;
+ @FXML
+ Label stateTitle;
+ HBox actionContainer;
+ private final List<Node> temporaryDragAreas = new ArrayList<>();
+
+ public void initialize() {
+ scroll.getStyleClass().clear();
+
+ HashMap<Symbol, Integer> res = new HashMap<>();
+ for (Symbol s : Symbol.getBasicResources()) {
+ res.put(s, 0);
+ }
+ setResources(res);
+ }
+
+ /**
+ * Set the displayed resources
+ *
+ * @param resources map to the resources amount
+ */
+ public void setResources(Map<Symbol, Integer> resources) {
+ resourcesCounter.getChildren().clear();
+ Symbol[] order = new Symbol[]{Symbol.FUNGUS, Symbol.PLANT, Symbol.ANIMAL, Symbol.INSECT,
+ Symbol.FEATHER, Symbol.PARCHMENT, Symbol.INKWELL};
+ for (Symbol s : order) {
+ ImageView icon = new ImageView(new Image("/images/symbols/" + s.toString().toUpperCase() + ".png"));
+ icon.setFitHeight(40);
+ icon.setFitWidth(40);
+ Label count = new Label(String.valueOf(resources.get(s)));
+ count.getStyleClass().add("resources-count");
+ resourcesCounter.getChildren().add(icon);
+ resourcesCounter.getChildren().add(count);
+ }
+ }
+
+ /**
+ * Places a card on the board
+ *
+ * @param coords
+ * @param card
+ * @param side
+ */
+ public void placeCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ playerBoard.addCard(coords, card, side);
+ }
+
+ /**
+ * Set the amount of points that the player has
+ * @param points amount of points
+ */
+ public void setPoints(int points) {
+ pointsCounter.setText("Points: " + points);
+ }
+
+ // Drag and drop management
+
+ /**
+ * Add attributes to the hand card
+ *
+ * @param card CardView to add attributes
+ */
+ private void initializeHandCard(CardView card) {
+ card.setCursor(Cursor.OPEN_HAND);
+ card.setOnDragDetected(event -> {
+ // Set Dragboard content
+ Dragboard dragboard = card.startDragAndDrop(TransferMode.MOVE);
+ ClipboardContent content = new ClipboardContent();
+ content.putString("");
+ dragboard.setContent(content);
+ // Set the card as image of the dragboard
+ card.setArc(0);
+ dragboard.setDragView(card.snapshot(null, null));
+ dragboard.setDragViewOffsetX(CardView.cardWidth / 2);
+ dragboard.setDragViewOffsetY(CardView.cardHeight / 2);
+ this.createDragArea((PlayableCard) card.getProperties().get("Card"), (Side) card.getProperties().get("Side"));
+ card.setVisible(false);
+ card.setCursor(Cursor.CLOSED_HAND);
+ event.consume();
+ });
+ card.setOnDragDone(event -> {
+ this.removeDragAreas();
+ card.setArc(20);
+ card.setVisible(true);
+ card.setCursor(Cursor.DEFAULT);
+ event.consume();
+ });
+ card.setOnMouseClicked((clickEvent) -> {
+ if (clickEvent.getButton() == MouseButton.SECONDARY) {
+ Side side = (Side) card.getProperties().get("Side");
+ Side newSide = side.equals(Side.BACK) ? Side.FRONT : Side.BACK;
+ card.setCard((PlayableCard) card.getProperties().get("Card"), newSide);
+ } else if (clickEvent.getButton() == MouseButton.MIDDLE) {
+ // Compatibility with Hyprland
+ if (this.temporaryDragAreas.size() > 0) {
+ this.removeDragAreas();
+ } else {
+ this.createDragArea((PlayableCard) card.getProperties().get("Card"), (Side) card.getProperties().get("Side"));
+ }
+ }
+ });
+ }
+
+ /**
+ * Create all possible drag areas on the board
+ *
+ * @param card Card to place
+ * @param side Side on which place the card
+ */
+ private void createDragArea(PlayableCard card, Side side) {
+ List<Pair<Integer, Integer>> placeableCoords = new ArrayList<>();
+ for (Pair<Integer, Integer> coords : playerBoard.takenSpots) {
+ for (int i = -1; i < 2; i++) {
+ for (int j = -1; j < 2; j++) {
+ if (i != 0 && j != 0) {
+ Pair<Integer, Integer> c = new Pair<>(coords.first() + i, coords.second() + j);
+ if (!playerBoard.takenSpots.contains(c)) placeableCoords.add(c);
+ }
+ }
+ }
+ }
+ for (Pair<Integer, Integer> c : placeableCoords) {
+ showDragArea(c, card, side);
+ }
+ }
+
+ /**
+ * Create a drag area in the specified coordinates, for the specified card on a certain side
+ *
+ * @param pcoords Game coordinates on which to put the area
+ * @param card Card where to put the area
+ * @param side Side on which to put the card
+ */
+ private void showDragArea(Pair<Integer, Integer> pcoords, PlayableCard card, Side side) {
+ Pane dragArea = new Pane();
+ dragArea.getStyleClass().add("place-spot");
+ // Set area position
+ Pair<Double, Double> gcoords = playerBoard.convertCoordinates(pcoords);
+ dragArea.setLayoutX(gcoords.first());
+ dragArea.setLayoutY(gcoords.second());
+ // Set area size
+ dragArea.setPrefHeight(CardView.cardHeight);
+ dragArea.setPrefWidth(CardView.cardWidth);
+
+ // When you drag over the area accept the drag
+ dragArea.setOnDragOver(event -> {
+ if (event.getGestureSource() != this && event.getDragboard().hasString()) {
+ event.acceptTransferModes(TransferMode.MOVE);
+ }
+ event.consume();
+ });
+ // When you drop on the area do the needed actions
+ dragArea.setOnDragDropped(event -> {
+ event.setDropCompleted(true);
+ this.removeDragAreas();
+ // Play the card
+ view.playCard(pcoords, card, side);
+ event.consume();
+ });
+ dragArea.setOnMouseClicked(event -> {
+ if (event.getButton().equals(MouseButton.PRIMARY)) {
+ this.removeDragAreas();
+ // Play the card
+ view.playCard(pcoords, card, side);
+ event.consume();
+ }
+ });
+
+ // When you enter the area change style
+ dragArea.setOnDragEntered(event -> {
+ dragArea.getStyleClass().add("place-spot-hover");
+ event.consume();
+ });
+ // When you exit the area change style
+ dragArea.setOnDragExited(event -> {
+ dragArea.getStyleClass().remove("place-spot-hover");
+ event.consume();
+ });
+ playerBoard.getChildren().add(dragArea);
+ temporaryDragAreas.add(dragArea);
+ }
+
+ /**
+ * Remove all temporary drag areas from player board
+ */
+ private void removeDragAreas() {
+ for (Node n : temporaryDragAreas) {
+ playerBoard.getChildren().remove(n);
+ }
+ temporaryDragAreas.clear();
+ }
+
+ /**
+ * Current player has to choose the secret objective
+ * @param objectives the two objectives to choose
+ */
+ public void giveSecretObjectives(Pair<Objective, Objective> objectives) {
+ stateTitle.setText("Choose your secret objective");
+ CardView first = new CardView(objectives.first(), Side.FRONT);
+ CardView second = new CardView(objectives.second(), Side.FRONT);
+ createCardChoiceContainer(first, second);
+ first.setCursor(Cursor.HAND);
+ second.setCursor(Cursor.HAND);
+ first.setOnMouseClicked((mouseEvent) -> view.chooseSecretObjective(objectives.first()));
+ second.setOnMouseClicked((mouseEvent -> view.chooseSecretObjective(objectives.second())));
+ }
+
+ /**
+ * Another player is choosing the secret objective
+ */
+ public void someoneDrewSecretObjective() {
+ stateTitle.setText(username + " is choosing the secret objective");
+ CardView first = new CardView(CardsManager.getInstance().getObjectives().get(1), Side.BACK);
+ CardView second = new CardView(CardsManager.getInstance().getObjectives().get(1), Side.BACK);
+ createCardChoiceContainer(first, second);
+ }
+
+ /**
+ * The current player is choosing the initial card side
+ * @param card given initial card
+ */
+ public void giveInitialCard(InitialCard card) {
+ stateTitle.setText("Choose your card");
+ CardView front = new CardView(card, Side.FRONT);
+ CardView back = new CardView(card, Side.BACK);
+ createCardChoiceContainer(front, back);
+ front.setCursor(Cursor.HAND);
+ front.setOnMouseClicked((e) -> {
+ view.chooseInitialCardSide(Side.FRONT);
+ });
+ back.setCursor(Cursor.HAND);
+ back.setOnMouseClicked((e) -> {
+ view.chooseInitialCardSide(Side.BACK);
+ });
+ }
+
+ /**
+ * Show that someone is choosing the initial card side
+ * @param card initial card
+ */
+ public void someoneDrewInitialCard(InitialCard card) {
+ stateTitle.setText(username + " is choosing the initial card side...");
+ CardView front = new CardView(card, Side.FRONT);
+ CardView back = new CardView(card, Side.BACK);
+ createCardChoiceContainer(front, back);
+ }
+
+ /**
+ * Remove the container that asks for initials card, objective cards..
+ */
+ public void removePlayerChoiceContainer() {
+ rootPane.getChildren().remove(actionContainer);
+ stateTitle.setText("");
+ }
+
+ /**
+ * Creates the container to show the choice of initials card or objectives
+ *
+ * @param front First option to show
+ * @param back Second option to show
+ */
+ private void createCardChoiceContainer(CardView front, CardView back) {
+ actionContainer = new HBox();
+ // Add CardViews
+ actionContainer.getChildren().add(front);
+ actionContainer.getChildren().add(back);
+ // Set container properties
+ actionContainer.setMaxHeight(Double.NEGATIVE_INFINITY);
+ actionContainer.setMaxWidth(Double.NEGATIVE_INFINITY);
+ actionContainer.setSpacing(30);
+ // Set alignment and add to the rootPane
+ actionContainer.setAlignment(Pos.BASELINE_CENTER);
+ rootPane.getChildren().add(actionContainer);
+ StackPane.setAlignment(actionContainer, Pos.CENTER);
+ }
+
+ /**
+ * Set the player username
+ * @param username username of the player
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ playerTab.getProperties().put("Username", username);
+ }
+
+ /**
+ * Get the Board Pane of the player
+ * @return the board pane
+ */
+ public BoardPane getBoard() {
+ return playerBoard;
+ }
+
+ /**
+ * Set the secret objective for the current player
+ * @param objective objective to set
+ */
+ public void setSecretObjective(Objective objective) {
+ CardView secretObjective;
+ if (objective != null) {
+ secretObjective = new CardView(objective, Side.FRONT);
+ } else {
+ secretObjective = new CardView(CardsManager.getInstance().getObjectives().get(2), Side.BACK);
+ }
+ StackPane.setAlignment(secretObjective, Pos.BOTTOM_LEFT);
+ StackPane.setMargin(secretObjective, new Insets(0, 100, 100, 0));
+
+ rootPane.getChildren().add(secretObjective);
+ }
+
+ /**
+ * Set state title, usually used to tell the player what to do
+ * @param title text of the title
+ */
+ public void setStateTitle(String title) {
+ this.stateTitle.setText(title);
+ }
+
+ /**
+ * Set player hand cards
+ * @param cards list of cards currently in the player's hand
+ */
+ public void setHandCards(List<PlayableCard> cards) {
+ handCards.getChildren().clear();
+ for (PlayableCard card : cards) {
+ CardView handCard = new CardView(card, Side.FRONT);
+ initializeHandCard(handCard);
+ handCards.getChildren().add(handCard);
+ }
+ }
+
+ /**
+ * Set if the player is the current one
+ * @param current if the player is current
+ */
+ public void setCurrentPlayer(boolean current) {
+ if (current) {
+ playerTab.getStyleClass().add("player-tab");
+ } else {
+ playerTab.getStyleClass().remove("player-tab");
+ }
+ }
+
+ /**
+ * Enables/disables mouse interactions with hand cards.
+ *
+ * @param enable True if interactions should be enabled, false otherwise
+ */
+ public void enablePlaceCardInteractions(boolean enable) {
+ handCards.setDisable(!enable);
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.html
new file mode 100644
index 00000000..9eeacacd
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.html
@@ -0,0 +1 @@
+RankingSceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.java.html
new file mode 100644
index 00000000..54a4bbff
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/RankingSceneController.java.html
@@ -0,0 +1,98 @@
+RankingSceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalApplication;
+import it.polimi.ingsw.client.frontend.gui.GraphicalViewGUI;
+import it.polimi.ingsw.utils.GuiUtil;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+
+import java.io.IOException;
+
+/**
+ * JavaFX Controller of the ranking scene, shows the leaderboard after the match is finished
+ */
+public class RankingSceneController extends SceneController {
+
+ public VBox leaderboardContainer;
+ public static double tableSize = 700;
+ public Label victoryLabel;
+ public Button playAgainButton;
+
+ @Override
+ public void initialize() throws IOException {
+
+ }
+
+ @Override
+ public void initializePostController() {
+ playAgainButton.setOnAction(event -> {
+ try {
+ showConnectionScene();
+ } catch (IOException e) {}
+ });
+ }
+
+ /**
+ * Add an entry to the graphical leaderboard
+ * @param entry the entry
+ */
+ public void addRanking(LeaderboardEntry entry) {
+ StackPane row = new StackPane();
+ row.setMaxHeight(Double.NEGATIVE_INFINITY);
+ row.setMinWidth(tableSize);
+
+ // Add player label
+ Label playerLabel = new Label(entry.username());
+ playerLabel.setMaxWidth(tableSize/3);
+ StackPane.setAlignment(playerLabel, Pos.CENTER_LEFT);
+ playerLabel.getStyleClass().add("leaderboard-player-label");
+
+ // Add player points
+ Label playerPoints = new Label(String.valueOf(entry.points()) + " points");
+ StackPane.setAlignment(playerPoints, Pos.CENTER);
+ playerPoints.getStyleClass().add("leaderboard-points");
+
+ // Add player result
+ Label playerResult = new Label(entry.winner() ? "Winner" : "Loser");
+ StackPane.setAlignment(playerResult, Pos.CENTER_RIGHT);
+ playerResult.getStyleClass().add("leaderboard-result");
+ playerResult.getStyleClass().add(entry.winner() ? "leaderboard-winner" : "leaderboard-loser");
+
+ // Add elements to parents
+ row.getChildren().addAll(playerLabel, playerPoints, playerResult);
+ leaderboardContainer.getChildren().add(row);
+ }
+
+ /**
+ * Set if the player has won the match
+ * @param victory if the current player has won
+ */
+ public void setVictory(boolean victory) {
+ if (victory) {
+ victoryLabel.setText("Victory");
+ } else {
+ victoryLabel.setText("Defeat");
+ }
+ }
+
+ /**
+ * Show the connection scene
+ * @throws IOException in case of file errors
+ */
+ public void showConnectionScene() throws IOException {
+ view.disconnect();
+ view = new GraphicalViewGUI(stage);
+ StackPane root = this.loadScene("/fxml/connection.fxml");
+ // Add stylesheet
+ GuiUtil.applyCSS(root, "/css/style.css");
+ // Create the connection scene
+ Scene connectionScene = new Scene(root, GraphicalApplication.screenWidth, GraphicalApplication.screenHeight);
+ stage.setScene(connectionScene);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.html
new file mode 100644
index 00000000..e0e73a7f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.html
@@ -0,0 +1 @@
+SceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.java.html
new file mode 100644
index 00000000..11c4c82b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/SceneController.java.html
@@ -0,0 +1,84 @@
+SceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalViewGUI;
+import it.polimi.ingsw.utils.GuiUtil;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+/**
+ * JavaFX controller of a scene
+ */
+public abstract class SceneController {
+ protected GraphicalViewGUI view;
+ protected Stage stage;
+
+ /**
+ * Method to be called to do actions after the controller attributes are set
+ * @throws IOException
+ */
+ public void initializePostController() throws IOException{
+ }
+
+ /**
+ * This method is run when the controller is initialized
+ * @throws IOException if there is a file error
+ */
+ public abstract void initialize() throws IOException;
+
+ /**
+ * Set the graphical view for the controller
+ * @param view graphical view
+ */
+ public void setGraphicalView(GraphicalViewGUI view) {
+ this.view = view;
+ }
+
+ /**
+ * Set the main stage the controller is in
+ * @param stage JavaFX stage
+ */
+ public void setStage(Stage stage) {
+ this.stage = stage;
+ }
+
+ /**
+ * Load a node from a FXML file path, assign properties to the controller
+ * and to the node
+ * @param path path of the FXML file
+ * @return the node on top of the FXML
+ * @param <T> Type of the node
+ * @throws IOException
+ */
+ protected <T extends Node>T loadScene(String path) throws IOException {
+ FXMLLoader loader = GuiUtil.getLoader(path);
+ T result = loader.load();
+ setControllerAttributes(loader, result);
+ return result;
+ }
+
+ /**
+ * Set graphical view and the main stage properties to the scene controller
+ * @param loader FXMLL Loader of the scene
+ * @throws IOException in case of exception
+ */
+ protected void setControllerAttributes(FXMLLoader loader) throws IOException {
+ SceneController controller = loader.getController();
+ controller.setGraphicalView(view);
+ controller.setStage(stage);
+ controller.initializePostController();
+ }
+ /**
+ * Set graphical view and the main stage properties to the scene controller
+ * also sets the "Controller" property for the given node
+ * @param loader FXMLL Loader of the scene
+ * @throws IOException in case of exception
+ */
+ protected void setControllerAttributes(FXMLLoader loader, Node node) throws IOException {
+ setControllerAttributes(loader);
+ node.getProperties().put("Controller", loader.getController());
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.html
new file mode 100644
index 00000000..eade1434
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.html
@@ -0,0 +1 @@
+WaitingSceneController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.java.html
new file mode 100644
index 00000000..62a5a9b4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/WaitingSceneController.java.html
@@ -0,0 +1,110 @@
+WaitingSceneController.java
package it.polimi.ingsw.client.frontend.gui.controllers;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalApplication;
+import it.polimi.ingsw.utils.GuiUtil;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * JavaFX controller for the waiting scene. It shows all the current players
+ * waiting for the match to start
+ */
+public class WaitingSceneController extends SceneController {
+
+ public Label matchName;
+ public VBox playersContainer;
+ private String name;
+ private int maxPlayers = 0;
+ private int players = 0;
+ private HashMap<String, Label> labels = new HashMap<>();
+
+ @Override
+ public void initialize() throws IOException {
+
+ }
+
+ /**
+ * Add a player to the list
+ * @param username username of the player to add
+ */
+ public void addPlayer(String username) {
+ Label playerLabel = new Label();
+ playerLabel.setAlignment(Pos.CENTER);
+ playerLabel.setText(username);
+ playerLabel.getStyleClass().add("form-label");
+ playersContainer.getChildren().add(playerLabel);
+ labels.put(username, playerLabel);
+ }
+
+ /**
+ * Set the name of the match to display
+ * @param name name of the match
+ */
+ public void setMatchName(String name) {
+ this.name = name;
+ updateLabel();
+ }
+
+ /**
+ * Set the maximum amount of players in the current match
+ * @param players maximum number of players
+ */
+ public void setMaxPlayers(int players) {
+ maxPlayers = players;
+ updateLabel();
+
+ }
+
+ /**
+ * Set the current amount of players in the current match
+ * @param players current number of players
+ */
+ public void setCurrentPlayers(int players) {
+ this.players = players;
+ updateLabel();
+ }
+
+ /**
+ * Get the amount of current players
+ * @return the amount of current players in the match
+ */
+ public int getCurrentPlayers() {
+ return players;
+ }
+
+ /**
+ * Update the players count, match name and max players
+ */
+ public void updateLabel() {
+ matchName.setText(name + " " + players + "/" + maxPlayers);
+ }
+
+ /**
+ * Remove a player from the list
+ * @param username username of the player to remove
+ */
+ public void removePlayer(String username) {
+ if (this.labels.containsKey(username)) {
+ playersContainer.getChildren().remove(labels.get(username));
+ }
+ }
+
+ /**
+ * Show the match scene when the match is started
+ * @return the match scene controller
+ * @throws IOException if there was a file error
+ */
+ public MatchSceneController showMatch() throws IOException {
+ VBox root = loadScene("/fxml/match.fxml");
+ GuiUtil.applyCSS(root, "/css/match.css");
+ Scene matchScene = new Scene(root, GraphicalApplication.screenWidth, GraphicalApplication.screenHeight);
+ stage.setScene(matchScene);
+ return (MatchSceneController) root.getProperties().get("Controller");
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.html
new file mode 100644
index 00000000..d5c895e9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui.controllers
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.source.html
new file mode 100644
index 00000000..aced26d2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.controllers/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui.controllers
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.html
new file mode 100644
index 00000000..101c3d4d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.html
@@ -0,0 +1 @@
+BoardPane
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.java.html
new file mode 100644
index 00000000..86305491
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/BoardPane.java.html
@@ -0,0 +1,91 @@
+BoardPane.java
package it.polimi.ingsw.client.frontend.gui.nodes;
+
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.CardsManager;
+import it.polimi.ingsw.utils.GuiUtil;
+import it.polimi.ingsw.utils.Pair;
+import javafx.scene.Node;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.Dragboard;
+import javafx.scene.input.TransferMode;
+import javafx.scene.layout.Pane;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Game board, manages card display
+ */
+public class BoardPane extends Pane {
+ // Card dimensions
+ public static double cardWidth = CardView.cardWidth;
+ public static double cardHeight = CardView.cardHeight;
+ public static double cardBorderW = 44.8;
+ public static double cardBorderH = 52.6;
+ // Currently placed cards
+ public List<Pair<Integer, Integer>> takenSpots = new ArrayList<>();
+
+ /**
+ * Constructor of BoardPane
+ */
+ public BoardPane() {
+ super();
+ }
+
+ /**
+ * Add a card to the board
+ * @param position relative coordinates of the card
+ * @param card card to add
+ * @param side side of the card to add
+ */
+ public void addCard(Pair<Integer, Integer> position, PlayableCard card, Side side) {
+ CardView c = new CardView(card, side);
+ c.getProperties().put("gameCoords", position);
+ displayCard(position, c);
+ takenSpots.add(position);
+ }
+
+ /**
+ * Add a card to the board
+ * @param position relative coordinates of the card
+ * @param card card to add
+ * @param side side of the card to add
+ * @return the added CardView
+ */
+ public CardView addCard(Pair<Integer, Integer> position, InitialCard card, Side side) {
+ CardView c = new CardView(card, side);
+ displayCard(position, c);
+ takenSpots.add(position);
+ c.getProperties().put("gameCoords", position);
+ return c;
+ }
+
+ /**
+ * Sets the layout coordinates of the card and adds it to the board
+ * @param position position of the card to place
+ * @param c CardView of the card
+ */
+ private void displayCard(Pair<Integer, Integer> position, CardView c) {
+ Pair<Double, Double> coords = convertCoordinates(position);
+ c.setLayoutX(coords.first());
+ c.setLayoutY(coords.second());
+ this.getChildren().add(c);
+ }
+
+ /**
+ * Convert coordinates from game coordinate to JavaFX Board coordinates
+ * @param coords coordinate to convert
+ * @return converted coordinates
+ */
+ public Pair<Double, Double> convertCoordinates(Pair<Integer, Integer> coords) {
+ double boardWidth = super.getPrefWidth();
+ double boardHeight = super.getPrefHeight();
+ double w = boardWidth/2 + (coords.first() * (cardWidth - cardBorderW));
+ double h = boardHeight/2 - (coords.second() * (cardHeight - cardBorderH));
+ return new Pair<>(w, h);
+ }
+
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.html
new file mode 100644
index 00000000..03cdacc9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.html
@@ -0,0 +1 @@
+CardView
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.java.html
new file mode 100644
index 00000000..4ba13c01
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/CardView.java.html
@@ -0,0 +1,252 @@
+CardView.java
package it.polimi.ingsw.client.frontend.gui.nodes;
+
+
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.GuiUtil;
+import it.polimi.ingsw.utils.Pair;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.ClipboardContent;
+import javafx.scene.input.Dragboard;
+import javafx.scene.input.TransferMode;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.paint.ImagePattern;
+import javafx.scene.shape.Rectangle;
+
+import javax.xml.crypto.dom.DOMCryptoContext;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CardView extends Pane {
+ // Card dimensions
+ public static double cardWidth = 199;
+ public static double cardHeight = 132;
+ public static double cardBorderW = 44.8;
+ public static double cardBorderH = 52.6;
+
+ /** Pawn dimensions (at center of the initial card) */
+ public static double tokenRadius = 28;
+
+ /** Path to what to show when a card is missing */
+ public static String noCardPath = "/images/no_resource2.png";
+ // Card corners
+ public Pane topLeftCorner;
+ public Pane topRightCorner;
+ public Pane bottomLeftCorner;
+ public Pane bottomRightCorner;
+
+ private Image image;
+ private Rectangle cardRectangle;
+
+ /**
+ * Initialize an empty CardView
+ */
+ public CardView() {
+ super();
+ String imagePath = noCardPath;
+ this.getProperties().put("Card", null);
+ this.getProperties().put("Side", null);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Initialize a CardView of an Initial Card
+ * @param card initial card
+ * @param side side to show
+ */
+ public CardView(InitialCard card, Side side) {
+ super();
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Initialize a CardView of a Playable Card
+ * @param card playable card
+ * @param side side to show
+ */
+ public CardView(PlayableCard card, Side side) {
+ super();
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Initialize a CardView of an Objective Card
+ * @param card objective
+ * @param side side to show
+ */
+ public CardView(Objective card, Side side) {
+ super();
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Change the displayed card
+ * @param card card to display
+ * @param side side of the card to display
+ */
+ public void setCard(InitialCard card, Side side) {
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Change the displayed card
+ * @param card card to display
+ * @param side side of the card to display
+ */
+ public void setCard(PlayableCard card, Side side) {
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Change the displayed card
+ * @param card card to display
+ * @param side side of the card to display
+ */
+ public void setCard(Objective card, Side side) {
+ String imagePath = GuiUtil.getImagePath(card, side);
+ this.getProperties().put("Card", card);
+ this.getProperties().put("Side", side);
+ this.addProperties(imagePath);
+ }
+
+ /**
+ * Change the displayed card to the back of a resource card
+ * @param reign reign to display
+ */
+ public void setResourcesCardBack(Symbol reign) {
+ String imagePath;
+ this.getProperties().put("Card", null);
+ this.getProperties().put("Side", Side.BACK);
+ if (reign != null) {
+ imagePath = GuiUtil.getResourcesBack(reign);
+ } else {
+ imagePath = noCardPath;
+ }
+ addProperties(imagePath);
+ }
+
+ /**
+ * Change the displayed card to the back of a gold card
+ * @param reign reign to display
+ */
+ public void setGoldsCardBack(Symbol reign) {
+ String imagePath;
+ this.getProperties().put("Card", null);
+ this.getProperties().put("Side", Side.BACK);
+ if (reign != null) {
+ imagePath = GuiUtil.getGoldsBack(reign);
+ } else {
+ imagePath = noCardPath;
+ }
+ addProperties(imagePath);
+ }
+
+ /**
+ * Add the properties to the image
+ * @param imagePath
+ */
+ private void addProperties(String imagePath) {
+ super.setPrefHeight(cardHeight);
+ super.setPrefWidth(cardWidth);
+ super.setMaxHeight(cardHeight);
+ super.setMaxWidth(cardWidth);
+ Rectangle rect = new Rectangle(cardWidth, cardHeight);
+ image = new Image(imagePath);
+ ImagePattern pattern = new ImagePattern(
+ image
+ );
+ rect.setFill(pattern);
+ cardRectangle = rect;
+ setArc(20);
+ super.getChildren().add(rect);
+ super.getStyleClass().add("game-card");
+ addCorners();
+ }
+
+ /**
+ * Set arcHeight and arcWidth of the card
+ * @param arc arc width and height
+ */
+ public void setArc(double arc) {
+ cardRectangle.setArcHeight(arc);
+ cardRectangle.setArcWidth(arc);
+ }
+
+
+ /**
+ * Add corners to the card
+ */
+ private void addCorners() {
+ topLeftCorner = new Pane();
+ topRightCorner = new Pane();
+ bottomLeftCorner= new Pane();
+ bottomRightCorner = new Pane();
+
+ setCornerProperties(topLeftCorner, 0, 0);
+ setCornerProperties(topRightCorner, cardWidth-cardBorderW,0);
+ setCornerProperties(bottomRightCorner, cardWidth-cardBorderW,cardHeight-cardBorderH);
+ setCornerProperties(bottomLeftCorner, 0,cardHeight-cardBorderH);
+ }
+
+ /**
+ * Add position and dimensions to a corner ad add them in the card
+ * @param corner The corner to add the properties to
+ * @param x position
+ * @param y position
+ */
+ private void setCornerProperties(Pane corner, double x, double y) {
+ corner.setPrefWidth(cardBorderW);
+ corner.setPrefHeight(cardBorderH);
+ corner.setLayoutX(x);
+ corner.setLayoutY(y);
+ super.getChildren().add(corner);
+ }
+
+ /**
+ * Display token of a color on the initial card
+ * @param color color of the token
+ */
+ public void setToken(Color color) {
+ double tokenY = 64;
+ Map<Side, Pair<Double, Double>> coords = new HashMap<>();
+ coords.put(Side.FRONT, new Pair<>(78.0, 121.0));
+ coords.put(Side.BACK, new Pair<>(62.0, 137.0));
+
+ Side side = (Side)super.getProperties().get("Side");
+
+ ImageView token = new ImageView(new Image(GuiUtil.getPawnImagePath(color)));
+ token.setFitWidth(tokenRadius);
+ token.setFitHeight(tokenRadius);
+
+ token.setLayoutX(coords.get(side).first() - tokenRadius/2);
+ token.setLayoutY(tokenY - tokenRadius / 2);
+
+ if (color.equals(Color.RED)) {
+ ImageView blackToken = new ImageView(GuiUtil.getBlackPawnImagePath());
+ blackToken.setFitWidth(tokenRadius);
+ blackToken.setFitHeight(tokenRadius);
+ blackToken.setLayoutX(coords.get(side).second() - tokenRadius/2);
+ blackToken.setLayoutY(tokenY - tokenRadius/2);
+ super.getChildren().add(blackToken);
+ }
+ super.getChildren().add(token);
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.html
new file mode 100644
index 00000000..0456d2a6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.html
@@ -0,0 +1 @@
+PlateauPane
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.java.html
new file mode 100644
index 00000000..5695785c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/PlateauPane.java.html
@@ -0,0 +1,130 @@
+PlateauPane.java
package it.polimi.ingsw.client.frontend.gui.nodes;
+
+import it.polimi.ingsw.gamemodel.Color;
+import it.polimi.ingsw.utils.GuiUtil;
+import it.polimi.ingsw.utils.Pair;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.Pane;
+
+import java.util.HashMap;
+
+/**
+ * JavaFX node, Pane showing all the player pawns and points
+ */
+public class PlateauPane extends Pane {
+ public static double pawnSize = 64;
+ public static double positionOffset = -6;
+ HashMap<Integer, Pair<Double, Double>> positions;
+ HashMap<String, ImageView> players;
+ HashMap<String, Integer> points;
+
+ /**
+ * Constructor of the node
+ */
+ public PlateauPane() {
+ players = new HashMap<>();
+ positions = new HashMap<>();
+ points = new HashMap<>();
+ positions.put(0, new Pair<>(104.0, 743.0));
+ positions.put(1, new Pair<>(197.0, 741.0));
+ positions.put(2, new Pair<>(291.0, 742.0));
+ positions.put(3, new Pair<>(342.0, 656.0));
+ positions.put(4, new Pair<>(246.0, 654.0));
+ positions.put(5, new Pair<>(154.0, 652.0));
+ positions.put(6, new Pair<>(56.0, 656.0));
+ positions.put(7, new Pair<>(57.0, 571.0));
+ positions.put(8, new Pair<>(152.0, 570.0));
+ positions.put(9, new Pair<>(247.0, 570.0));
+ positions.put(10, new Pair<>(338.0, 569.0));
+ positions.put(11, new Pair<>(340.0, 482.0));
+ positions.put(12, new Pair<>(243.0, 482.0));
+ positions.put(13, new Pair<>(150.0, 483.0));
+ positions.put(14, new Pair<>(55.0, 486.0));
+ positions.put(15, new Pair<>(56.0, 397.0));
+ positions.put(16, new Pair<>(151.0, 397.0));
+ positions.put(17, new Pair<>(244.0, 395.0));
+ positions.put(18, new Pair<>(340.0, 398.0));
+ positions.put(19, new Pair<>(340.0, 313.0));
+ positions.put(20, new Pair<>(200.0, 270.0));
+ positions.put(21, new Pair<>(60.0, 315.0));
+ positions.put(22, new Pair<>(59.0, 227.0));
+ positions.put(23, new Pair<>(60.0, 140.0));
+ positions.put(24, new Pair<>(114.0, 71.0));
+ positions.put(25, new Pair<>(198.0, 54.0));
+ positions.put(26, new Pair<>(287.0, 70.0));
+ positions.put(27, new Pair<>(342.0, 141.0));
+ positions.put(28, new Pair<>(342.0, 225.0));
+ positions.put(29, new Pair<>(199.0, 158.0));
+ }
+
+ /**
+ * Set the color of a player
+ *
+ * @param player username of the player
+ * @param color color of the player's pawn
+ */
+ public void setColor(String player, Color color) {
+ ImageView img = new ImageView(new Image(GuiUtil.getPawnImagePath(color)));
+ img.setFitWidth(pawnSize);
+ img.setFitHeight(pawnSize);
+ players.put(player, img);
+ setPoints(player, 0);
+ }
+
+ /**
+ * Set the amount of points of a player and move its pawn
+ *
+ * @param player username of the player
+ * @param points current number of points he has
+ */
+ public void setPoints(String player, int points) {
+ if (points > 29) {
+ return;
+ }
+ if (!this.points.containsKey(player) || this.points.get(player) != points) {
+ Pair<Double, Double> position = convertCoords(positions.get(points));
+ // Calculate offset because of players in the same position
+ int offset = this.playersAtPosition(points);
+ this.points.put(player, points);
+
+ ImageView playerPawn = players.get(player);
+ playerPawn.setLayoutX(position.first());
+ playerPawn.setLayoutY(position.second() + offset * positionOffset);
+
+ if (this.getChildren().contains(playerPawn))
+ this.getChildren().remove(playerPawn);
+ this.getChildren().add(playerPawn);
+ }
+ }
+
+ /**
+ * Get the amount of players in a certain point
+ *
+ * @param points number of points
+ * @return number of players in that position
+ */
+ private int playersAtPosition(int points) {
+ int p = 0;
+ for (String player : this.points.keySet()) {
+ if (points == this.points.get(player)) {
+ p++;
+ }
+ }
+ return p;
+ }
+
+ /**
+ * Convert relative coordinates of the pane to coordinates
+ * corrected to the pawn size
+ *
+ * @param coord coordinate to convert
+ * @return the converted coordinates
+ */
+ private Pair<Double, Double> convertCoords(Pair<Double, Double> coord) {
+ return new Pair<>(coord.first() - pawnSize / 2 + 2, coord.second() - pawnSize / 2);
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.html
new file mode 100644
index 00000000..18e501ad
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui.nodes
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.source.html
new file mode 100644
index 00000000..9b18d79b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui.nodes/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui.nodes
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.html
new file mode 100644
index 00000000..1a8cbce3
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.html
@@ -0,0 +1 @@
+GraphicalApplication
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.java.html
new file mode 100644
index 00000000..2bdaeea4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalApplication.java.html
@@ -0,0 +1,87 @@
+GraphicalApplication.java
package it.polimi.ingsw.client.frontend.gui;
+
+import it.polimi.ingsw.client.frontend.gui.controllers.SceneController;
+import it.polimi.ingsw.utils.GuiUtil;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import java.io.IOException;
+
+/**
+ * Class from which the FXML application is run, so it's entry point for the user.
+ * Apart from making use of FXML instances and methods, it interacts massively with {@link GraphicalViewGUI}.
+ */
+public class GraphicalApplication extends Application {
+ private GraphicalViewGUI view;
+ private Stage primaryStage;
+ // Window size
+ public static double screenWidth = 1920.0;
+ public static double screenHeight = 1020.0;
+
+ /**
+ * Launch the application
+ * @param args args
+ */
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ /**
+ * Default method to start the FXML application, it can be called only from this class main(...).
+ *
+ * @param primaryStage The stage to be opened
+ * @throws IOException If there has been an I/O error.
+ */
+ @Override
+ public void start(Stage primaryStage) throws IOException {
+ this.primaryStage = primaryStage;
+ this.view = new GraphicalViewGUI(primaryStage);
+
+ // Load initial screen
+ primaryStage.setTitle("Codex Naturalis");
+ // Load FXML layout
+ StackPane root = this.loadScene("/fxml/connection.fxml");
+ // Add stylesheet
+ GuiUtil.applyCSS(root, "/css/style.css");
+ // Create the connection scene
+ Scene connectionScene = new Scene(root, screenWidth, screenHeight);
+ // Show the window
+
+ /* Disabled by default
+ // Fullscreen
+ primaryStage.setFullScreen(true);
+ primaryStage.setMaximized(true);
+ primaryStage.setResizable(false);
+ primaryStage.setFullScreenExitHint("");
+ */
+
+ primaryStage.setScene(connectionScene);
+ primaryStage.show();
+ root.requestFocus();
+ primaryStage.setOnCloseRequest((event) -> {
+ Platform.exit();
+ System.exit(0);
+ });
+ }
+
+
+ /**
+ * Get a node from the specified FXML path and set the values of a SceneController
+ * @param path file path of FXML
+ * @return The first node
+ * @param <T> Type of the node
+ * @throws IOException in case of error while reading the file
+ */
+ private <T>T loadScene(String path) throws IOException {
+ FXMLLoader loader = GuiUtil.getLoader(path);
+ T result = loader.load();
+ SceneController controller = loader.getController();
+ controller.setGraphicalView(view);
+ controller.setStage(primaryStage);
+ return result;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.html
new file mode 100644
index 00000000..e206021a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.html
@@ -0,0 +1 @@
+GraphicalViewGUI
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.java.html
new file mode 100644
index 00000000..663c68f1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/GraphicalViewGUI.java.html
@@ -0,0 +1,511 @@
+GraphicalViewGUI.java
package it.polimi.ingsw.client.frontend.gui;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.client.frontend.ClientBoard;
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.client.frontend.MatchStatus;
+import it.polimi.ingsw.client.frontend.ShownCard;
+import it.polimi.ingsw.client.frontend.gui.controllers.*;
+import it.polimi.ingsw.client.frontend.gui.nodes.CardView;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.*;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+
+/**
+ * JavaFX implementation of Codex Naturalis client
+ */
+public class GraphicalViewGUI extends GraphicalView {
+ private final Stage stage;
+ // Controllers
+ private Map<String, PlayerTabController> playerTabControllers;
+ private MatchSceneController matchSceneController;
+ private WaitingSceneController waitingSceneController;
+ private LobbySceneController lobbySceneController;
+ private RankingSceneController rankingSceneController;
+ private ChatPaneController chatPaneController;
+
+ // Match state management
+ MatchStatus matchState = MatchStatus.LOBBY;
+
+ // Temporary vars
+ private String matchName;
+ private List<AvailableMatch> lastAvailableMatches;
+ private Integer maxPlayers;
+
+ /**
+ * Initialize on a given JavaFX stage
+ * @param stage the main stage of the application
+ */
+ public GraphicalViewGUI(Stage stage) {
+ this.stage = stage;
+ }
+
+ @Override
+ public void changePlayer() {
+ Platform.runLater(() -> {
+ // Notify to each player tab if it is his turn and disable his hand card interactions
+ // this is needed in order to disable this client's interactions after his turn has finished
+ for (String username : playerTabControllers.keySet()) {
+ PlayerTabController tab = playerTabControllers.get(username);
+ boolean isCurrent = username.equals(currentPlayer);
+
+ tab.setCurrentPlayer(isCurrent);
+ tab.enablePlaceCardInteractions(false);
+ }
+ });
+ }
+
+ /**
+ * Method called everytime it's this client turn.
+ */
+ @Override
+ public void makeMove() {
+ this.changePlayer();
+ Platform.runLater(() -> {
+ matchSceneController.setFocus(this.username);
+
+ // Enable the hand cards interactions, so that they can be dragged
+ playerTabControllers.get(this.username).enablePlaceCardInteractions(true);
+ playerTabControllers.get(this.username).setStateTitle("Play a card");
+ });
+ }
+
+ @Override
+ public void createMatch(String matchName, Integer maxPlayers) {
+ super.createMatch(matchName, maxPlayers);
+ this.matchName = matchName;
+ this.maxPlayers = maxPlayers;
+ }
+
+ @Override
+ public void joinMatch(String matchName) {
+ super.joinMatch(matchName);
+ this.matchName = matchName;
+ }
+
+ @Override
+ protected void notifyMatchStarted() {
+ this.setupMatch(false, false);
+ }
+
+ @Override
+ protected void notifyMatchResumed(boolean drawPhase) {
+ this.setupMatch(true, drawPhase);
+ }
+
+ /**
+ * Set match scene and populate elements on match start
+ * @param matchResumed if the match is resumed
+ */
+ private void setupMatch(boolean matchResumed, boolean drawPhase) {
+ matchState = MatchStatus.MATCH_STATE;
+ Platform.runLater(() -> {
+ try {
+ if (waitingSceneController == null) {
+ waitingSceneController = new WaitingSceneController();
+ waitingSceneController.setGraphicalView(this);
+ waitingSceneController.setStage(stage);
+ }
+ matchSceneController = waitingSceneController.showMatch();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ // Set visible objectives
+ matchSceneController.setObjectives(super.visibleObjectives);
+ // Set visible draw sources
+ super.visiblePlayableCards.forEach((drawSource, playableCard) -> {
+ matchSceneController.setDrawSource(drawSource, playableCard, playableCard.getReign());
+ });
+ matchSceneController.setDrawSource(DrawSource.GOLDS_DECK, null, super.decksTopReign.first());
+ matchSceneController.setDrawSource(DrawSource.RESOURCES_DECK, null, super.decksTopReign.second());
+
+ // Create players tabs, assign colors and their hands
+ int n = 0;
+ playerTabControllers = new HashMap<>();
+ for (String p : super.players) {
+ try {
+ PlayerTabController controller = matchSceneController.addPlayerTab(p, Color.values()[n]);
+ playerTabControllers.put(p, controller);
+ controller.setHandCards(super.clientBoards.get(p).getHand());
+ // Disable the interaction with hand cards on all player tabs
+ controller.enablePlaceCardInteractions(false);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ n++;
+ }
+
+ // Initialize the chat pane
+ chatPaneController = matchSceneController.getChatPane();
+ playerTabControllers.forEach((tabUsername, controller) -> {
+ if (!tabUsername.equals(this.username))
+ chatPaneController.addPlayer(tabUsername);
+ });
+
+ // By default, disable draw sources interactions
+ matchSceneController.enableDrawSourcesInteractions(false);
+ if (matchResumed) this.setupResumedMatch(drawPhase);
+ });
+ }
+
+ /**
+ * Populate extra elements after match resumed
+ */
+ private void setupResumedMatch(boolean drawPhase) {
+ playerTabControllers.forEach(((username, playerTabController) -> {
+ ClientBoard playerBoard = clientBoards.get(username);
+ playerTabController.setSecretObjective(playerBoard.getObjective());
+
+ // Place the initial card
+ playerTabController.getBoard().addCard(new Pair<>(0, 0), (InitialCard) playerBoard.getPlaced().get(0).card(), playerBoard.getPlaced().get(0).side());
+
+ // Place all the other cards
+ Map<Integer, ShownCard> placed = playerBoard.getPlaced();
+ for (Integer turn : placed.keySet()) {
+ if (turn > 0) {
+ playerTabController.getBoard().addCard(placed.get(turn).coords(), (PlayableCard) placed.get(turn).card(), placed.get(turn).side());
+ }
+ }
+
+ // Set points and available resources
+ playerTabController.setPoints(playerBoard.getPoints());
+ matchSceneController.plateauPane.setPoints(username, playerBoard.getPoints());
+ playerTabController.setResources(playerBoard.getAvailableResources());
+ }));
+
+ // Enable interactions if it is the current user turn
+ this.changePlayer();
+ if (currentPlayer.equals(username)) {
+ if (!drawPhase) {
+ this.makeMove();
+ playerTabControllers.get(username).enablePlaceCardInteractions(true);
+ } else {
+ // Draw interactions
+ // Set focus on the table
+ matchSceneController.setFocusToTable();
+ matchSceneController.setStateTitle("Draw a card");
+ // Enable draw sources interactions
+ matchSceneController.enableDrawSourcesInteractions(true);
+ }
+ }
+ }
+
+ @Override
+ public void giveInitialCard(InitialCard initialCard) {
+ super.giveInitialCard(initialCard);
+ this.changePlayer();
+ Platform.runLater(() -> {
+ playerTabControllers.get(username).giveInitialCard(initialCard);
+ matchSceneController.setFocus(username);
+ });
+ }
+
+ @Override
+ public void giveSecretObjectives(Pair<Objective, Objective> secretObjectives) {
+ super.giveSecretObjectives(secretObjectives);
+ this.changePlayer();
+ Platform.runLater(() -> {
+ playerTabControllers.get(username).giveSecretObjectives(secretObjectives);
+ matchSceneController.setFocus(username);
+ });
+ }
+
+ @Override
+ public void someoneDrewInitialCard(String someoneUsername, InitialCard card) {
+ super.someoneDrewInitialCard(someoneUsername, card);
+ Platform.runLater(() -> playerTabControllers.get(someoneUsername).someoneDrewInitialCard(card));
+ }
+
+ @Override
+ public void someoneSetInitialSide(String someoneUsername, Side side, Map<Symbol, Integer> availableResources) {
+ super.someoneSetInitialSide(someoneUsername, side, availableResources);
+ Platform.runLater(() -> {
+ PlayerTabController playerTabController = playerTabControllers.get(someoneUsername);
+ playerTabController.removePlayerChoiceContainer();
+ InitialCard card = super.clientBoards.get(someoneUsername).getInitialCard();
+ CardView initial = playerTabController.getBoard().addCard(new Pair<>(0, 0), card, side);
+ initial.setToken(Color.values()[players.indexOf(someoneUsername)]);
+ });
+ }
+
+ @Override
+ public void someoneDrewSecretObjective(String someoneUsername) {
+ super.someoneDrewSecretObjective(someoneUsername);
+ PlayerTabController playerTabController = playerTabControllers.get(someoneUsername);
+ Platform.runLater(playerTabController::someoneDrewSecretObjective);
+ }
+
+ @Override
+ public void someoneChoseSecretObjective(String someoneUsername) {
+ super.someoneChoseSecretObjective(someoneUsername);
+ Platform.runLater(() -> {
+ PlayerTabController playerTabController = playerTabControllers.get(someoneUsername);
+ playerTabController.removePlayerChoiceContainer();
+ if (someoneUsername.equals(username)) {
+ playerTabController.setSecretObjective(clientBoards.get(username).getObjective());
+ } else {
+ playerTabController.setSecretObjective(null);
+ }
+ });
+ }
+
+ @Override
+ public void notifyLastTurn() {
+ for (PlayerTabController t : playerTabControllers.values()) {
+ Platform.runLater(() -> t.setStateTitle("Last turn, play carefully!"));
+ }
+ }
+
+ @Override
+ public void someoneJoined(String someoneUsername, List<String> joinedPlayers) {
+ if (!matchState.equals(MatchStatus.WAIT_STATE) && !matchState.equals(MatchStatus.LOBBY)) {
+ return;
+ }
+ super.someoneJoined(someoneUsername, joinedPlayers);
+ if (this.maxPlayers == null) {
+ maxPlayers = lastAvailableMatches.stream()
+ .filter((m) -> m.name().equals(matchName))
+ .mapToInt(AvailableMatch::maxPlayers)
+ .toArray()[0];
+ }
+ if (username.equals(someoneUsername)) {
+ matchState = MatchStatus.WAIT_STATE;
+ Platform.runLater(() -> {
+ try {
+ waitingSceneController = lobbySceneController.showWaitScene();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ waitingSceneController.setCurrentPlayers(joinedPlayers.size());
+ waitingSceneController.setMatchName(matchName);
+ waitingSceneController.setMaxPlayers(maxPlayers);
+ for (String player : joinedPlayers) {
+ waitingSceneController.addPlayer(player);
+ }
+ });
+ } else {
+ Platform.runLater(() -> {
+ waitingSceneController.addPlayer(someoneUsername);
+ waitingSceneController.setCurrentPlayers(joinedPlayers.size());
+ });
+ }
+ }
+
+ @Override
+ public void someoneQuit(String someoneUsername) {
+ if (matchState.equals(MatchStatus.WAIT_STATE)) {
+ Platform.runLater(() -> {
+ waitingSceneController.removePlayer(someoneUsername);
+ waitingSceneController.setCurrentPlayers(waitingSceneController.getCurrentPlayers()-1);
+ });
+ } else if (networkHandler.isConnected() && !matchState.equals(MatchStatus.FINAL_STATE)) {
+ notifyError("Player Quit", "Match finished because " + someoneUsername + " quit");
+ }
+ }
+
+ @Override
+ public void matchFinished(List<LeaderboardEntry> ranking) {
+ Platform.runLater(() -> {
+ try {
+ matchState = MatchStatus.FINAL_STATE;
+ rankingSceneController = matchSceneController.showRankingScene();
+ ranking.forEach((entry) -> {
+ if (entry.username().equals(this.username)) {
+ rankingSceneController.setVictory(entry.winner());
+ }
+ rankingSceneController.addRanking(entry);
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void someoneSentBroadcastText(String someoneUsername, String text) {
+ Platform.runLater(() -> {
+ if (someoneUsername.equals(this.username))
+ chatPaneController.confirmSubmitBroadcastMessage(text);
+ else
+ chatPaneController.receiveBroadcastMessage(someoneUsername, text);
+ });
+ }
+
+ @Override
+ public void someoneSentPrivateText(String someoneUsername, String text) {
+ Platform.runLater(() -> {
+ if (someoneUsername.equals(this.username))
+ chatPaneController.confirmSubmitPrivateMessage(text);
+ else
+ chatPaneController.receivePrivateMessage(someoneUsername, text);
+ });
+ }
+
+ @Override
+ public void someonePlayedCard(String someoneUsername, Pair<Integer, Integer> coords, PlayableCard card, Side side, int points, Map<Symbol, Integer> availableResources) {
+ super.someonePlayedCard(someoneUsername, coords, card, side, points, availableResources);
+ Platform.runLater(() -> {
+ PlayerTabController controller = playerTabControllers.get(someoneUsername);
+ controller.placeCard(coords, card, side);
+ controller.setPoints(points);
+ matchSceneController.setPlateauPoints(someoneUsername, points);
+ controller.setHandCards(clientBoards.get(someoneUsername).getHand());
+ controller.setResources(availableResources);
+
+ // If the player that played a card is this client
+ if (someoneUsername.equals(this.username)) {
+ // Set the focus on the plateau tab
+ matchSceneController.setFocusToTable();
+ matchSceneController.setStateTitle("Draw a card");
+ // Enable draw sources interactions
+ matchSceneController.enableDrawSourcesInteractions(true);
+ } else {
+ matchSceneController.setStateTitle(someoneUsername + " is drawing a card...");
+ }
+ playerTabControllers.get(someoneUsername).setStateTitle("");
+ });
+ }
+
+ @Override
+ public void someoneDrewCard(String someoneUsername, DrawSource source, PlayableCard card, PlayableCard replacementCard,
+ Pair<Symbol, Symbol> deckTopReigns) {
+ super.someoneDrewCard(someoneUsername, source, card, replacementCard, deckTopReigns);
+ Platform.runLater(() -> {
+ PlayerTabController tab = playerTabControllers.get(someoneUsername);
+ tab.setHandCards(clientBoards.get(someoneUsername).getHand());
+ if (!source.equals(DrawSource.GOLDS_DECK) && !source.equals(DrawSource.RESOURCES_DECK)) {
+ matchSceneController.setDrawSource(source, replacementCard, replacementCard.getReign());
+ }
+ matchSceneController.setDrawSource(DrawSource.GOLDS_DECK, null, deckTopReigns.first());
+ matchSceneController.setDrawSource(DrawSource.RESOURCES_DECK, null, deckTopReigns.second());
+
+ // If the player that drew a card is this client, disable draw source interactions
+ if (someoneUsername.equals(this.username)) {
+ matchSceneController.enableDrawSourcesInteractions(false);
+ matchSceneController.setFocus(this.username);
+ }
+ // Remove draw title
+ matchSceneController.setStateTitle("");
+ });
+ }
+
+ @Override
+ public void notifyError(Exception exception) {
+ this.notifyError(GuiUtil.getExceptionTitle(exception), exception.getMessage());
+ }
+
+ /**
+ * Notify an error
+ * @param title title of the error
+ * @param description description of the error
+ */
+ public void notifyError(String title, String description) {
+ Platform.runLater(() -> {
+ try {
+ // Load the error node from the fxml file
+
+ FXMLLoader loader = GuiUtil.getLoader("/fxml/error.fxml");
+ StackPane root = loader.load();
+ ErrorSceneController controller = loader.getController();
+
+ Stage dialog = new Stage();
+ Scene errorScene = new Scene(root, ErrorSceneController.windowWidth, ErrorSceneController.windowHeight);
+
+ // Initialize attributes
+ GuiUtil.applyCSS(root, "/css/style.css");
+ controller.setTitle(title);
+ controller.setText(description);
+
+ dialog.setScene(errorScene);
+ dialog.setTitle("Error");
+ dialog.initOwner(this.stage);
+ dialog.initModality(Modality.APPLICATION_MODAL);
+ dialog.setResizable(false);
+ dialog.sizeToScene();
+
+ // Show the modal window (stage)
+ dialog.show();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ networkHandler.setUsername(username);
+ }
+
+ /**
+ * Getter for the client username
+ * @return client username
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Request available matches to the server
+ */
+ public void getAvailableMatches() {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.getAvailableMatches();
+ }
+
+ @Override
+ public void receiveAvailableMatches(List<AvailableMatch> availableMatches) {
+ super.receiveAvailableMatches(availableMatches);
+ lastAvailableMatches = availableMatches;
+ Platform.runLater(() -> lobbySceneController.updateMatches(availableMatches));
+ }
+
+ /**
+ * Set the lobby scene controller
+ * @param lobbySceneController controller of the lobby scene
+ */
+ public void setLobbySceneController(LobbySceneController lobbySceneController) {
+ this.lobbySceneController = lobbySceneController;
+ this.getAvailableMatches();
+ }
+
+ /**
+ * Main class to launch the applicaiton
+ * @param args command line arguments
+ */
+ public static void main(String[] args) {
+ Application.launch(GraphicalApplication.class, args);
+ }
+
+ @Override
+ public void notifyConnectionLost() {
+ notifyError(new RemoteException("Connection to the server lost"));
+ RankingSceneController r = new RankingSceneController();
+ Platform.runLater(() -> {
+ try {
+ r.setStage(stage);
+ r.setGraphicalView(this);
+ r.showConnectionScene();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ /**
+ * Request disconnection from the network
+ */
+ public void disconnect() {
+ networkHandler.disconnect();
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.html
new file mode 100644
index 00000000..d8e41f83
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.source.html
new file mode 100644
index 00000000..98049298
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.gui/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.gui
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.html
new file mode 100644
index 00000000..89967cc2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.html
@@ -0,0 +1 @@
+BoardPosition
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.java.html
new file mode 100644
index 00000000..85743155
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/BoardPosition.java.html
@@ -0,0 +1,17 @@
+BoardPosition.java
package it.polimi.ingsw.client.frontend.tui;
+
+import java.util.Optional;
+import it.polimi.ingsw.gamemodel.Corner;
+
+/**
+ * Represents a position on the board, used to find valid positions and display anchor numbers when
+ * a player must choose where to place the card.
+ *
+ * @param isValid Whether the position is a valid one or not
+ * @param link The anchor point for a valid position
+ *
+ * @see ValidPositions
+ */
+public record BoardPosition(boolean isValid, Optional<Corner> link) {
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.html
new file mode 100644
index 00000000..f8596353
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.html
@@ -0,0 +1 @@
+GraphicalViewTUI
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.java.html
new file mode 100644
index 00000000..8b1340c7
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/GraphicalViewTUI.java.html
@@ -0,0 +1,948 @@
+GraphicalViewTUI.java
package it.polimi.ingsw.client.frontend.tui;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.client.frontend.ClientBoard;
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.client.frontend.ShownCard;
+import it.polimi.ingsw.client.network.NetworkHandlerRMI;
+import it.polimi.ingsw.client.network.NetworkHandlerTCP;
+import it.polimi.ingsw.exceptions.WrongInputFormatException;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.RequestStatus;
+
+/**
+ * Class that handles client game loop from TUI.
+ */
+public class GraphicalViewTUI extends GraphicalView {
+ private final TuiPrinter printer;
+ private String lastError;
+ private boolean ongoing;
+ private List<String> playersWithObjective;
+ private final PlayerControls playerControls;
+ private final InputHandler inputHandler;
+ private final ValidPositions validPositions;
+ private final static List<String> helpMessage = List.of(
+ "players, p -> show list of players",
+ "write, w -> write message (add :username to send private text)",
+ "chat, c -> show chat",
+ "board, b -> show your board (or specify a number to show corresponding player's board)",
+ "objectives, o -> show secret and common objectives",
+ "hand, h -> show your hand");
+
+ private List<String> chat;
+ private List<String> messages;
+ private final static String playerControlPrompt =
+ "Type command, or 'help' for a list of available commands.";
+
+
+ /**
+ * Class constructor. Starts the interface and creates all auxiliary objects.
+ */
+ public GraphicalViewTUI() {
+ super();
+ this.ongoing = true;
+ this.playersWithObjective = new ArrayList<>();
+ this.playerControls = new PlayerControls(); // starts disabled
+ this.validPositions = new ValidPositions();
+
+ this.chat = new ArrayList<>();
+ this.messages = new ArrayList<>();
+ try {
+ this.printer = new TuiPrinter();
+ } catch (Exception e) {
+ throw new RuntimeException("Could not access terminal. Quitting now");
+ }
+
+ this.inputHandler = new InputHandler(this.printer);
+
+ }
+
+ /**
+ * Actually starts the interface, then handles the pre-match.
+ */
+ private void startInterface() {
+ this.printer.clearTerminal();
+ this.setNetworkHandler();
+ this.printer.clearTerminal();
+ this.setMatch();
+ new Thread(this::startPlayerControls).start();
+ }
+
+ ///////////////////////
+ // AUXILIARY METHODS //
+ ///////////////////////
+ /**
+ * Sets the last request's status, eventually notifying all threads of the server's response.
+ *
+ * @param status The last request's status
+ */
+ @Override
+ public void setLastRequestStatus(RequestStatus status) {
+ synchronized (this.lastRequest) {
+ super.setLastRequestStatus(status);
+ if (!status.equals(RequestStatus.PENDING)) {
+ this.lastRequest.notifyAll();
+ }
+ }
+ }
+
+
+ /**
+ * Waits for the server response after any action was performed. Until the server does not send
+ * a response (or an error), the thread is stopped.
+ *
+ * @return Whether the last action was successful or not
+ */
+ private boolean getServerResponse() {
+ this.printer.printCenteredMessage("Waiting for server...", 1);
+ try {
+ synchronized (this.lastRequest) {
+ while (this.lastRequest.getStatus().equals(RequestStatus.PENDING)) {
+ this.lastRequest.wait();
+ }
+ }
+ return this.lastRequest.getStatus().equals(RequestStatus.SUCCESSFUL);
+ } catch (InterruptedException e) {
+ throw new RuntimeException();
+ }
+
+ }
+
+
+ /**
+ * Shows the player's board and hand.
+ *
+ * @param board the player's board
+ */
+ private synchronized void showHand(ClientBoard board) {
+ this.printer.printPlayerBoard(this.username, board);
+ this.printer.printHandAtBottom(board.getHand());
+ }
+
+ /**
+ * Asks the user which card he wants to play.
+ *
+ * @param board The player's board
+ *
+ * @return The chosen card
+ */
+ private PlayableCard chooseCardFromHand(ClientBoard board) {
+ List<PlayableCard> hand = board.getHand();
+
+ this.inputHandler.setPrompt("Choose card to play (1, 2, 3):");
+ this.showHand(board);
+ String userIn = this.inputHandler.askUser();
+
+ PlayableCard card = null;
+ Integer maxValue = hand.size();
+ while (card == null) {
+ try {
+ Integer index = Integer.parseInt(userIn) - 1;
+ if (index >= 0 && index < maxValue) {
+ card = hand.get(index);
+ } else {
+ throw new NumberFormatException("Number not in range!");
+ }
+ } catch (NumberFormatException e) {
+ this.inputHandler.setPrompt("Not a valid number! try again");
+ this.showHand(board);
+ userIn = this.inputHandler.askUser();
+ }
+ }
+
+ return card;
+ }
+
+
+ /**
+ * Asks the user which side he wants to play the chosen card.
+ *
+ * @param card The card to be placed on the board
+ *
+ * @return The chosen side
+ */
+ private Side chooseCardSide(PlayableCard card) {
+ this.printer.clearTerminal();
+ this.printer.printPlayableFrontAndBack(card, 0);
+
+ this.inputHandler
+ .setPrompt("What side do you want to play the card on? (defaults to front)");
+ String userIn = this.inputHandler.askUser();
+ return switch (userIn) {
+ case "b", "back" -> Side.BACK;
+ default -> Side.FRONT;
+ };
+ }
+
+
+ /**
+ * Asks the user where he wants to play the chosen card.
+ *
+ * @param board The current player's board
+ *
+ * @return The chosen coordinates
+ */
+ private Pair<Integer, Integer> chooseCoords(ClientBoard board) {
+ Map<Pair<Integer, Integer>, Pair<Integer, Corner>> valids =
+ this.validPositions.getValidPlaces();
+
+ Pair<Integer, Integer> coord = null;
+
+ this.inputHandler.setPrompt("Choose where to place card:");
+ while (coord == null) {
+ this.printer.printValidPlaces(valids);
+ this.printer.printPlayerBoard(this.username, board);
+
+ Integer position = -1;
+ try {
+ position = Integer.valueOf(this.inputHandler.askUser());
+ } catch (NumberFormatException e) {
+ this.inputHandler.setPrompt("Not a number! Try again");
+ }
+ if (position != -1) {
+ for (Pair<Integer, Integer> cmp : valids.keySet()) {
+ if (valids.get(cmp).first().equals(position)) {
+ coord = cmp;
+ }
+ }
+ this.inputHandler.setPrompt("Not a valid number! try again");
+ }
+ }
+ return coord;
+ }
+
+
+ /**
+ * Gets the player's input while it's not his turn, and then performs the corresponding action.
+ */
+ private void parsePlayerControl() {
+ ClientBoard board = this.clientBoards.get(this.username);
+ String userIn, command, argument, player;
+
+ userIn = this.inputHandler.getNextLine();
+ this.printer.clearTerminal();
+
+ int splitIndex = userIn.indexOf(" ");
+ if (splitIndex == -1) {
+ command = userIn;
+ argument = "";
+ } else {
+ command = userIn.substring(0, splitIndex);
+ argument = userIn.substring(splitIndex + 1);
+ }
+
+ switch (command) {
+ case "o", "objectives":
+ this.printer.printObjectives(username, board.getColor(), board.getObjective(),
+ this.visibleObjectives);
+ break;
+ case "h", "hand":
+ this.printer.printHand(this.username, board.getColor(), board.getHand());
+ break;
+ case "b", "board":
+ switch (argument) {
+ case "1":
+ player = this.players.get(0);
+ this.printer.printPlayerBoard(player, this.clientBoards.get(player));
+ break;
+ case "2":
+ player = this.players.get(1);
+ this.printer.printPlayerBoard(player, this.clientBoards.get(player));
+ break;
+ case "3":
+ if (this.players.size() > 2) {
+ player = this.players.get(2);
+ this.printer.printPlayerBoard(player, this.clientBoards.get(player));
+ }
+ break;
+ case "4":
+ if (this.players.size() > 3) {
+ player = this.players.get(3);
+ this.printer.printPlayerBoard(player, this.clientBoards.get(player));
+ }
+ break;
+
+ default:
+ this.printer.printPlayerBoard(this.username,
+ this.clientBoards.get(this.username));
+ break;
+ }
+ break;
+ case "c", "chat":
+ this.printer.printChat(this.chat);
+ break;
+ case "w", "write":
+ if (!argument.equals("")) {
+ if (argument.charAt(0) == ':') {
+ splitIndex = argument.indexOf(" ");
+ if (splitIndex != -1) {
+ String text = argument.substring(splitIndex + 1);
+ String recipient = argument.substring(1, splitIndex);
+ if (!argument.equals("")) {
+ this.sendPrivateText(recipient, text);
+ }
+ if (!this.getServerResponse()) {
+ this.messages.add(this.lastError);
+ } else {
+ this.chat.add("(to: " + recipient + "): " + text);
+ }
+ }
+ } else {
+ this.sendBroadcastText(argument);
+ if (!this.getServerResponse()) {
+ this.messages.add(this.lastError);
+ } else {
+ this.chat.add("(you): " + argument);
+ }
+ }
+
+ this.printer.clearTerminal();
+ }
+ break;
+ case "p", "players":
+ this.printer.printSimpleList(this.players, false, true);
+ break;
+
+ case "help":
+ this.printer.printSimpleList(helpMessage, false, false);
+ break;
+
+ default:
+ this.printer.printCenteredMessage("Not a known command: '" + command
+ + "'. Type 'help' to show a help message", 0);
+ break;
+ }
+
+ this.inputHandler.setPrompt(playerControlPrompt);
+ this.inputHandler.showPrompt();
+ }
+
+
+ /**
+ * Enables the player to use custom commands while it's not his turn.
+ */
+ private void enablePlayerControls() {
+ this.inputHandler.setPrompt(playerControlPrompt);
+ this.inputHandler.showPrompt();
+ this.playerControls.enable();
+ }
+
+
+ /**
+ * Starts the handling of player controls. If enables, polls for user input and as soon as there
+ * is something calls {@link GraphicalViewTUI#parsePlayerControl()}. If disabled, stops the
+ * thread
+ */
+ private void startPlayerControls() {
+ while (this.ongoing) {
+ synchronized (this.playerControls) {
+ if (this.playerControls.isEnabled()) {
+ try {
+ if (System.in.available() > 0) {
+ this.parsePlayerControl();
+ } else {
+ Thread.sleep(200);
+ }
+ } catch (InterruptedException e) {
+ } catch (IOException e) {
+ System.err.println("Could not connect! Quitting now...");
+ System.exit(1);
+ }
+ } else {
+ try {
+ this.playerControls.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ }
+ }
+ }
+
+ ////////////////////////
+ // PRE MATCH METHODS //
+ ///////////////////////
+ /**
+ * Sets the network view, asking the player how he wants to connect (host, port, RMI vs TCP).
+ */
+ private void setNetworkHandler() {
+ String userIn, IPAddr;
+ Integer port = null;
+
+ this.inputHandler.setPrompt("Choose IP address:");
+ IPAddr = this.inputHandler.askUser();
+ this.inputHandler.setPrompt("Choose port:");
+ userIn = this.inputHandler.askUser();
+ while (port == null) {
+ try {
+ port = Integer.valueOf(userIn);
+ } catch (NumberFormatException e) {
+ userIn = this.inputHandler.askUser();
+ }
+ }
+
+ this.inputHandler.setPrompt("Choose connection type (1 for TCP, 2 for RMI)");
+ this.networkHandler = null;
+ while (this.networkHandler == null) {
+ userIn = this.inputHandler.askUser();
+ try {
+ switch (userIn) {
+ case "1", "tcp", "TCP":
+ this.setNetworkHandler(new NetworkHandlerTCP(this, IPAddr, port));
+ break;
+ case "2", "rmi", "RMI":
+ this.setNetworkHandler(new NetworkHandlerRMI(this, IPAddr, port));
+ break;
+ default:
+ this.inputHandler.setPrompt(
+ "Not a valid connection type! Choose connection type (1 for TCP, 2 for RMI)");
+ break;
+ }
+ } catch (Exception e) {
+ this.printer.clearTerminal();
+ this.printer.printMessage("Could not connect! Try again");
+ this.setNetworkHandler();
+ return;
+ }
+ }
+ }
+
+
+ /**
+ * Asks the player to choose a username.
+ */
+ private void chooseUsername() {
+ String userIn = "";
+ this.inputHandler.setPrompt("Choose username:");
+ while (userIn.equals("")) {
+ userIn = this.inputHandler.askUser();
+ this.inputHandler.setPrompt("Not a valid username! Choose username:");
+ }
+ super.setUsername(userIn);
+ }
+
+
+ /**
+ * Asks the server for a list of available matches and waits for it.
+ */
+ private void getAvailableMatches() {
+ this.lastRequest.setStatus(RequestStatus.PENDING);
+ this.networkHandler.getAvailableMatches();
+
+ if (!this.getServerResponse()) {
+ this.printer.clearTerminal();
+ this.printer.printCenteredMessage("Could not receive availbale matches, try again!", 1);
+ this.getAvailableMatches();
+ return;
+ }
+ }
+
+
+ /**
+ * Tries to create a new match, asking the player for match name and max players.
+ *
+ * @throws WrongInputFormatException if the max number of players was not specified
+ */
+ private void createMatch() throws WrongInputFormatException {
+ String userIn = this.inputHandler.askUser();
+ Integer splitIndex = userIn.indexOf(" ");
+ if (splitIndex == -1) {
+ throw new WrongInputFormatException("The max players number was not specified!");
+ }
+
+ String matchName = userIn.substring(0, splitIndex);
+ Integer maxPlayers;
+ try {
+ maxPlayers = Integer.valueOf(userIn.substring(splitIndex + 1));
+ } catch (Exception e) {
+ throw new WrongInputFormatException("Bad format for max players number!");
+ }
+
+ super.createMatch(matchName, maxPlayers);
+ }
+
+
+ /**
+ * Tries to join a match, showing the player a list of matches and their relative index, and
+ * asking him which he wants to join.
+ *
+ * @param joinables List of matches the player can join
+ *
+ * @throws WrongInputFormatException if the player did not specify a valid index
+ */
+ private void joinMatch(List<AvailableMatch> joinables) throws WrongInputFormatException {
+ String userIn = this.inputHandler.askUser();
+ Integer matchIndex;
+ try {
+ matchIndex = Integer.valueOf(userIn) - 1;
+ } catch (NumberFormatException e) {
+ throw new WrongInputFormatException("You must specify a number!");
+ }
+
+ if (matchIndex < 0 || matchIndex >= joinables.size()) {
+ throw new WrongInputFormatException("Invalid index!");
+ }
+
+ super.joinMatch(joinables.get(matchIndex).name());
+ }
+
+
+ /**
+ * Tries to set the match, either creating it or joining an already existing one.
+ *
+ * @see GraphicalViewTUI#joinMatch(List)
+ * @see GraphicalViewTUI#createMatch()
+ */
+ private void setMatch() {
+ List<AvailableMatch> joinables = new ArrayList<>(), notJoinables = new ArrayList<>();
+ this.chooseUsername();
+
+ this.getAvailableMatches();
+
+ String createMatchPrompt = "Type the match name and max players (e.g. MatchTest 2).";
+ String joinMatchPrompt = "Type the number corresponding to the match you want to join.";
+
+ this.availableMatches.forEach(match -> {
+ if (match.currentPlayers() < match.maxPlayers() || match.isRejoinable()) {
+ joinables.add(match);
+ } else {
+ notJoinables.add(match);
+ }
+ });
+ this.printer.clearTerminal();
+
+ boolean matchSet = false;
+
+ while (!matchSet) {
+ try {
+ if (this.availableMatches.isEmpty()) {
+ this.inputHandler.setPrompt("No matches available. " + createMatchPrompt);
+ this.createMatch();
+ matchSet = true;
+ } else {
+ if (joinables.isEmpty())
+ joinMatchPrompt = "No matches available. " + joinMatchPrompt;
+
+ this.inputHandler.setPrompt(
+ "Do you want to join a match or (c)reate one? (defaults to join)");
+ String userIn = this.inputHandler.askUser();
+ this.printer.printMatchesLobby(joinables, notJoinables, 0);
+ switch (userIn) {
+ case "c", "C", "create", "Create":
+ this.inputHandler.setPrompt(createMatchPrompt);
+ this.createMatch();
+ matchSet = true;
+ break;
+
+ default:
+ this.inputHandler.setPrompt(joinMatchPrompt);
+ this.joinMatch(joinables);
+ matchSet = true;
+ break;
+ }
+ }
+ } catch (WrongInputFormatException e) {
+ this.inputHandler.setPrompt(e.getMessage() + "! Try again.");
+ }
+ }
+
+ if (!this.getServerResponse()) {
+ this.printer.clearTerminal();
+ this.printer.printCenteredMessage(this.lastError + "! Try again.", 0);
+ this.setMatch();
+ return;
+ }
+
+ }
+
+
+ /**
+ * Show the list of players in the match.
+ *
+ * @param someoneUsername Last player who joined the match
+ * @param joinedPlayers List of all the other players
+ */
+ @Override
+ public void someoneJoined(String someoneUsername, List<String> joinedPlayers) {
+ super.someoneJoined(someoneUsername, joinedPlayers);
+ this.printer.clearTerminal();
+ this.printer.printWelcomeScreen();
+ this.messages.add("Joined players:");
+ this.messages.addAll(joinedPlayers);
+ this.printer.printListReverse(this.messages);
+ this.printer.printPrompt("");
+ this.messages.clear();
+ }
+
+ ///////////////////
+ // MATCH METHODS //
+ ///////////////////
+ /**
+ * Asks the user which side he wants to play the initial card.
+ *
+ * @param initialCard The initial card he drew
+ */
+ @Override
+ public void giveInitialCard(InitialCard initialCard) {
+ super.giveInitialCard(initialCard);
+ this.printer.clearTerminal();
+ this.printer.printInitialSideBySide(initialCard, 1);
+
+ this.inputHandler.setPrompt("Choose initial card side (defaults to front)");
+ String userIn = this.inputHandler.askUser();
+ Side side;
+ switch (userIn) {
+ case "b", "back":
+ side = Side.BACK;
+ break;
+ default:
+ side = Side.FRONT;
+ break;
+ }
+
+ super.chooseInitialCardSide(side);
+ if (!this.getServerResponse()) {
+ this.giveInitialCard(initialCard);
+ } else {
+ this.printer.clearTerminal();
+ this.validPositions
+ .addCard(new ShownCard(initialCard, side, new Pair<Integer, Integer>(0, 0)));
+ }
+ }
+
+
+ /**
+ * Asks the user which secret objective he wants to keep between the random two given to him.
+ *
+ * @param secretObjectives the pair of objectives the player has to choose from
+ */
+ @Override
+ public void giveSecretObjectives(Pair<Objective, Objective> secretObjectives) {
+ super.giveSecretObjectives(secretObjectives);
+ this.printer.clearTerminal();
+ this.printer.printObjectivePair("Your choices:", secretObjectives, 1);
+
+ this.inputHandler.setPrompt("Choose secret objective (defaults to first):");
+ String userIn = this.inputHandler.askUser();
+ Objective objective;
+ switch (userIn) {
+ case "2", "second":
+ objective = secretObjectives.second();
+ break;
+ default:
+ objective = secretObjectives.first();
+ break;
+ }
+
+ super.chooseSecretObjective(objective);
+ if (!this.getServerResponse()) {
+ this.giveSecretObjectives(secretObjectives);
+ }
+ }
+
+
+ /**
+ * Adds to the list of player with objectives the last player who chose his secret objective.
+ * This is used to determine whether the player currently playing has already chosen secret
+ * objective (and so should play a regular turn) or not.
+ *
+ * @param someoneUsername the username of the last player who chose secret objective
+ */
+ @Override
+ public void someoneChoseSecretObjective(String someoneUsername) {
+ super.someoneChoseSecretObjective(someoneUsername);
+ this.playersWithObjective.add(someoneUsername);
+ }
+
+
+ /**
+ * Notifies all players (but the current one) that someone is playing their turn.
+ */
+ @Override
+ public void changePlayer() {
+ this.printer.clearTerminal();
+ ClientBoard board = this.clientBoards.get(this.currentPlayer);
+
+ new Thread(() -> {
+ if (board.getPlaced().isEmpty()) { // choosing initial side
+ this.printer.printCenteredMessage(this.currentPlayer + " is choosing initial side!",
+ 0);
+ this.printer.printPrompt("");
+ } else if (!this.playersWithObjective.contains(this.currentPlayer)) { // choosing
+ // objective
+ this.printer.printCenteredMessage(
+ this.currentPlayer + " is choosing secret objective!", 0);
+ this.printer.printPrompt("");
+ } else {
+ this.enablePlayerControls();
+ }
+ }).start();
+ }
+
+
+ /**
+ * Ask a player to choose a card to play, the side on which the card should be played, and the
+ * coordinates in which the card should be played; finally trying to actually play the card.
+ */
+ @Override
+ public void makeMove() {
+ this.playerControls.disable();
+ this.printer.clearTerminal();
+
+ if (this.lastRequest.getStatus().equals(RequestStatus.FAILED)) {
+ this.messages.add(lastError + " Try again.");
+ }
+ if (this.lastTurn) {
+ this.messages.add("This is the last turn! Play carefully");
+ }
+ if (!this.messages.isEmpty()) {
+ this.printer.printMessages(messages);
+ }
+
+ ClientBoard board = this.clientBoards.get(this.username);
+ PlayableCard card = this.chooseCardFromHand(board);
+ Side side = this.chooseCardSide(card);
+ Pair<Integer, Integer> coords = this.chooseCoords(board);
+ ShownCard shownCard = new ShownCard(card, side, coords);
+
+ this.printer.clearTerminal();
+ this.printer.printPlayerBoard(this.username, board);
+ this.printer.printCard(shownCard);
+
+ this.inputHandler.setPrompt("Are you sure? (n to cancel)");
+ String userIn = this.inputHandler.askUser();
+ if (userIn.equals("n")) {
+ this.makeMove();
+ return;
+ }
+
+ super.playCard(coords, card, side);
+ if (!this.getServerResponse()) {
+ this.printer.clearTerminal();
+ this.printer.clearTerminal();
+ this.makeMove();
+ return;
+ } else {
+ this.messages.clear();
+ this.validPositions.addCard(shownCard);
+ }
+ }
+
+
+ /**
+ * Asks the player from where he wants to draw.
+ *
+ * @param availableResources All the possible draw sources
+ */
+ private void makeUserDraw(Map<Symbol, Integer> availableResources) {
+ this.printer.clearTerminal();
+ DrawSource source = null;
+ this.printer.printAvailableResources(availableResources, 0);
+ String userIn;
+ this.inputHandler.setPrompt("Choose a draw source: ");
+ while (source == null) {
+ this.printer.printDrawingScreen(decksTopReign, visiblePlayableCards);
+
+ this.inputHandler.setPrompt("Choose draw source:");
+ userIn = this.inputHandler.askUser();
+ switch (userIn) {
+ case "G", "g":
+ source = DrawSource.GOLDS_DECK;
+ break;
+ case "R", "r":
+ source = DrawSource.RESOURCES_DECK;
+ break;
+ case "1":
+ source = DrawSource.FIRST_VISIBLE;
+ break;
+ case "2":
+ source = DrawSource.SECOND_VISIBLE;
+ break;
+ case "3":
+ source = DrawSource.THIRD_VISIBLE;
+ break;
+ case "4":
+ source = DrawSource.FOURTH_VISIBLE;
+ break;
+ default:
+ this.inputHandler.setPrompt("Not a valid source! Try again.");
+ break;
+ }
+ }
+ super.drawCard(source);
+ if (!getServerResponse()) {
+ this.makeUserDraw(availableResources);
+ return;
+ }
+ }
+
+
+ /**
+ * Notifies all players that someone played a card, and updates the relative player's board.
+ *
+ * @param someoneUsername The player that played the card
+ * @param coords The chosen coordinates
+ * @param card The chosen played card
+ * @param side The chosen side
+ * @param points The points of that player
+ * @param availableResources The resources of that player
+ */
+ @Override
+ public void someonePlayedCard(String someoneUsername, Pair<Integer, Integer> coords,
+ PlayableCard card, Side side, int points, Map<Symbol, Integer> availableResources) {
+ super.someonePlayedCard(someoneUsername, coords, card, side, points, availableResources);
+
+ if (this.username.equals(someoneUsername)) {
+ this.makeUserDraw(availableResources);
+ }
+ }
+
+
+ /**
+ * Notifies everyone else that a player left.
+ *
+ * @param someoneUsername Player's username
+ */
+ @Override
+ public void someoneQuit(String someoneUsername) {
+ this.printer.printCenteredMessage(someoneUsername + " quit!", 0);
+ }
+
+
+ /**
+ * Shows whether the current player won or lost.
+ *
+ * @param ranking Match ranking
+ */
+ @Override
+ public void matchFinished(List<LeaderboardEntry> ranking) {
+ this.printer.clearTerminal();
+ this.ongoing = false;
+ this.printer.printEndScreen(ranking, this.username);
+ }
+
+
+ /**
+ * Sets the last error message to what the server responded.
+ *
+ * @param exception The thrown exception
+ */
+ @Override
+ public void notifyError(Exception exception) {
+ super.notifyError(exception);
+ this.lastError = exception.getMessage();
+ if (this.lastError == null) {
+ this.lastError = exception.getClass().getName();
+ }
+ }
+
+
+ /**
+ * Notifies that the match has started.
+ */
+ @Override
+ protected void notifyMatchStarted() {}
+
+
+ /**
+ * Notifies that the player correctly rejoined a match, and makes him play his turn.
+ *
+ * @param drawPhase whether the player should draw or play
+ */
+ @Override
+ protected void notifyMatchResumed(boolean drawPhase) {
+ new Thread(() -> {
+
+ this.clientBoards.get(this.username).getPlaced()
+ .forEach((turn, shownCard) -> this.validPositions.addCard(
+ new ShownCard(shownCard.card(), shownCard.side(), shownCard.coords())));
+
+ // we resume match only if the game was in progress, so all players chose secret
+ // objectives
+ this.players.forEach(this.playersWithObjective::add);
+
+ this.printer.clearTerminal();
+ if (this.username.equals(this.currentPlayer)) {
+ if (drawPhase) {
+ this.makeUserDraw(this.clientBoards.get(this.username).getAvailableResources());
+ // new Thread(() ->
+ // this.makeUserDraw(this.clientBoards.get(this.username).getAvailableResources())).start();;
+ } else {
+ // new Thread(this::makeMove).start();
+ this.makeMove();
+ }
+ } else {
+ this.enablePlayerControls();
+ }
+
+ }).start();
+ }
+
+
+ /**
+ * Adds to the chat a broadcast text.
+ *
+ * @param someoneUsername The player who sent the broadcast
+ * @param text The sent text
+ */
+ @Override
+ public void someoneSentPrivateText(String someoneUsername, String text) {
+ super.someoneSentPrivateText(someoneUsername, text);
+
+ if (!this.username.equals(someoneUsername)) {
+ this.chat.add("(" + someoneUsername + "): " + text);
+ this.messages.add(someoneUsername + " sent a private text!");
+ this.printer.printMessages(this.messages);
+ this.inputHandler.showPrompt();
+ }
+ }
+
+
+ /**
+ * Adds to the chat a private text.
+ *
+ * @param someoneUsername The player who sent the private text
+ * @param text The sent text
+ */
+ @Override
+ public void someoneSentBroadcastText(String someoneUsername, String text) {
+ super.someoneSentBroadcastText(someoneUsername, text);
+
+ if (!this.username.equals(someoneUsername)) {
+ this.chat.add("[" + someoneUsername + "]: " + text);
+ this.messages.add(someoneUsername + " sent a text!");
+ this.printer.printMessages(this.messages);
+ this.inputHandler.showPrompt();
+ }
+ }
+
+ /**
+ * Notifies that there has been a connection error. We only care about server crashes, but it
+ * could be anything
+ */
+ @Override
+ public void notifyConnectionLost() {
+ this.printer.clearTerminal();
+ this.printer.printCenteredMessage("Connection Lost!", 0);
+ System.exit(1);
+ }
+
+
+ /**
+ * Launch the TUI client
+ * @param args command line arguments
+ */
+ public static void main(String[] args) {
+ GraphicalViewTUI tui = new GraphicalViewTUI();
+ tui.startInterface();
+ while (tui.ongoing) {
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.html
new file mode 100644
index 00000000..dcc78066
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.html
@@ -0,0 +1 @@
+InputHandler
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.java.html
new file mode 100644
index 00000000..1fd6c3f8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/InputHandler.java.html
@@ -0,0 +1,80 @@
+InputHandler.java
package it.polimi.ingsw.client.frontend.tui;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * Class that handles the prompt and gets the user input
+ */
+public class InputHandler {
+ private final TuiPrinter printer;
+ private final Scanner scanner;
+ private String prompt;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param printer The actual printer
+ */
+ public InputHandler(TuiPrinter printer) {
+ this.printer = printer;
+ this.scanner = new Scanner(System.in);
+ }
+
+
+ /**
+ * Sets the current prompt, without displaying it.
+ *
+ * @param prompt The new value
+ */
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+
+ /**
+ * Gets the next user input.
+ *
+ * @return The next user input
+ */
+ public String getNextLine() {
+ return this.scanner.nextLine();
+ }
+
+
+ /**
+ * Shows the current prompt.
+ */
+ public void showPrompt() {
+ this.printer.printPrompt(this.prompt);
+ }
+
+
+ /**
+ * Flushes the stdin, so that every input from the user before showing the prompt gets discarded.
+ */
+ private void clearStdin() {
+ try {
+ System.in.read(new byte[System.in.available()]);
+ } catch (IOException e) {
+ this.printer.printCenteredMessage("Disconnected! Looks like the game is gone", 0);
+ }
+ }
+
+
+ /**
+ * Flushes stdin, prints prompt and then gets the user input.
+ *
+ * @return The user input
+ */
+ public String askUser() {
+ this.clearStdin();
+ this.printer.printPrompt(this.prompt);
+ String userIn = this.scanner.nextLine();
+ this.printer.clearTerminal();
+ return userIn;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.html
new file mode 100644
index 00000000..16d477d3
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.html
@@ -0,0 +1 @@
+PlayerControls
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.java.html
new file mode 100644
index 00000000..c4f2b542
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/PlayerControls.java.html
@@ -0,0 +1,42 @@
+PlayerControls.java
package it.polimi.ingsw.client.frontend.tui;
+
+/**
+ * Class used to synchronyze methods that should be ran only during other player's turns.
+ */
+public class PlayerControls {
+ private boolean enabled;
+
+
+ /**
+ * Class constructor. Starts disabled.
+ */
+ public PlayerControls() {
+ this.enabled = false;
+ }
+
+
+ /**
+ * @return Whether the player's custom controls are enabled or not.
+ */
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+
+ /**
+ * Enables player's custom controls, and notifies all threads synchronized on this object.
+ */
+ public synchronized void enable() {
+ this.enabled = true;
+ this.notifyAll();
+ }
+
+
+ /**
+ * Disables the player's controls.
+ */
+ public void disable() {
+ this.enabled = false;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.html
new file mode 100644
index 00000000..99252497
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.html
@@ -0,0 +1 @@
+TuiPrinter
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.java.html
new file mode 100644
index 00000000..c8d8816f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/TuiPrinter.java.html
@@ -0,0 +1,1225 @@
+TuiPrinter.java
package it.polimi.ingsw.client.frontend.tui;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.jline.terminal.Terminal;
+import it.polimi.ingsw.client.frontend.ClientBoard;
+import it.polimi.ingsw.client.frontend.ShownCard;
+import it.polimi.ingsw.exceptions.CardException;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.TUICardParser;
+
+/**
+ * Class that handles the actual printing to the terminal.
+ */
+public class TuiPrinter {
+ private final Terminal terminal;
+ private final TUICardParser parser;
+ private final Integer infoLineOffset;
+ private static final Integer cardRows = 6, cardCols = 18, cornerRows = 3, cornerCols = 5;
+
+ /**
+ * Class constructor, it creates auxiliary objects to communicate with the terminal and the card
+ * parser.
+ *
+ * @throws IOException
+ */
+ public TuiPrinter() throws IOException {
+ this.terminal = org.jline.terminal.TerminalBuilder.terminal();
+ this.parser = new TUICardParser();
+ this.infoLineOffset = 2;
+
+ }
+
+ // ! PRIVATE METHODS //
+ /**
+ * Sums two coordinates.
+ *
+ * @param op1 The first coordinate
+ * @param op2 The second coordinate
+ *
+ * @return A new coordinate containing the sum of the two
+ */
+ private Pair<Integer, Integer> sumCoords(Pair<Integer, Integer> op1,
+ Pair<Integer, Integer> op2) {
+ return new Pair<>(op1.first() + op2.first(), op1.second() + op2.second());
+ }
+
+ /**
+ * @return The terminal's height (rows).
+ */
+ private Integer getHeight() {
+ return this.terminal.getHeight();
+ }
+
+ /**
+ * @return The terminal's width (columns).
+ */
+ private Integer getWidth() {
+ return this.terminal.getWidth();
+ }
+
+
+ /**
+ * Sets the string's position on the terminal. This is done via an escape sequence, that anchors
+ * the text in the specified row and column.
+ *
+ * @param x The x coordinate (column)
+ * @param y The y coordinate (row)
+ *
+ * @return The escape sequence to be inserted in the string, in order for it to be anchored in
+ * the right spot
+ */
+ private String setPosition(Integer x, Integer y) {
+ return "\033[" + y + ";" + x + "H";
+ }
+
+ private Pair<Integer, Integer> getAbsoluteCoords(Pair<Integer, Integer> coords) {
+ int termRows = this.getHeight(), termCols = this.getWidth();
+
+ Pair<Integer, Integer> coordOffset =
+ new Pair<Integer, Integer>((termCols - cardCols) / 2, (termRows - cardRows) / 2);
+ Pair<Integer, Integer> coordUpdated =
+ new Pair<Integer, Integer>(coords.first() * (cardCols - cornerCols),
+ -coords.second() * (cardRows - cornerRows));
+
+ return this.sumCoords(coordOffset, coordUpdated);
+ }
+
+ /**
+ * Prints the colored username of a player.
+ *
+ * @param username The player's username
+ * @param color The color associated to the player
+ *
+ * @return A string with the necessary escape sequences to show a colored username
+ */
+ private String parseUsername(String username, Color color) {
+ String c = switch (color) {
+ case Color.RED -> "\033[031m";
+ case Color.YELLOW -> "\033[032m";
+ case Color.GREEN -> "\033[033m";
+ case Color.BLUE -> "\033[034m";
+ default -> "";
+ };
+
+ return c + username + "\033[0m";
+ }
+
+ /**
+ * Shows a card on the terminal
+ *
+ * @param card Record containing the card and its coordinates
+ */
+ public void printCard(ShownCard card) {
+ try {
+ if (card.coords().equals(new Pair<>(0, 0)))
+ System.out.println(parser.parseCard(card.card(), getAbsoluteCoords(card.coords()),
+ null, card.side() == Side.FRONT));
+ else
+ System.out.println(parser.parseCard(card.card(), getAbsoluteCoords(card.coords()),
+ card.coords(), card.side() == Side.FRONT));
+ System.out.println("\033[0m");
+ } catch (CardException e) {
+ }
+ }
+
+ /**
+ * Prints the user points and username.
+ *
+ * @param username The player's username
+ * @param color The player's associated color
+ * @param points The player's point
+ */
+ private void printPoints(String username, Color color, Integer points) {
+ int oldOffset = this.getHeight() - infoLineOffset;
+ int termCols = this.getWidth();
+ // int newOffset = 1;
+ String out = this.parseUsername(username, color) + "'s points: " + points;
+ System.out.println(this.setPosition((termCols - out.length()) / 4, oldOffset) + out);
+ }
+
+ private int getDimStart(int max, int dim) {
+ if (dim >= max)
+ return 1;
+
+ int left = max - dim; // available space
+ if (left % 2 == 1)
+ left--;
+ return left / 2; // starting coord
+ }
+
+
+ /**
+ * Prints the "welcome" string.
+ *
+ * @param x The starting x coordinate
+ * @param y The starting y coordinate
+ */
+ private void printWelcome(int x, int y) {
+ List<String> welcomeString = new ArrayList<>();
+
+ String prefix = setPosition(x, y);
+ welcomeString.add(prefix
+ + " _ __ __ __ ");
+ prefix = setPosition(x, ++y);
+ welcomeString.add(prefix
+ + " | | / / ___ / / _____ ____ ____ ___ ___ / /_ ____ ");
+ prefix = setPosition(x, ++y);
+ welcomeString.add(prefix
+ + " | | /| / / / _ \\ / / / ___/ / __ \\ / __ __ \\ / _ \\ / __/ / __ \\ ");
+ prefix = setPosition(x, ++y);
+ welcomeString.add(prefix
+ + " | |/ |/ / / __/ / / / /__ / /_/ / / / / / / / / __/ / /_ / /_/ / ");
+ prefix = setPosition(x, ++y);
+ welcomeString.add(prefix
+ + " |__/|__/ \\___/ /_/ \\___/ \\____/ /_/ /_/ /_/ \\___/ \\__/ \\____/ ");
+
+ for (int i = 0; i < welcomeString.size(); i++)
+ System.out.println(welcomeString.get(i));
+ }
+
+ /**
+ * Prints either the small or big title.
+ *
+ * @param x The starting x coordinate
+ * @param y The starting y coordinate
+ * @param isLittle Whether the title should be small or big
+ */
+ private void printTitle(int x, int y, boolean isLittle) {
+ if (isLittle)
+ printLittleTitle(x, y);
+ else
+ printBigTitle(x, y);
+ }
+
+ /**
+ * Prints the big title.
+ *
+ * @param x The starting x coordinate
+ * @param y The starting y coordinate
+ */
+ private void printBigTitle(int x, int y) {
+ String white = "\033[0m", yellow = parser.getRightColor(Symbol.INKWELL), bold = "\033[1m";
+ List<String> titleString = new ArrayList<>();
+
+ String prefix = setPosition(x, y);
+ titleString.add(prefix
+ + " _____ _____ ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " ( ___ )---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------( ___ ) ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | ... .. ... ... s .. . .x+=:. | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | xH88''~ .x8X dF .=*8888n..'%888: :8 x .d88' @88> z' ^% | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | :8888 .f'8888Hf u. '88bu. uL .. X |8888f '8888 .88 x. . .u . 5888R %8P . <k | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | :8888> X8L ^''' ...ue888b '*88888bu .u .@88b @88R 88x. '8888X 8888> u :888ooo .@88k z88u .d88B :@8c u '888R . .@8Ned8' | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | X8888 X888h 888R Y888r ^'*8888N ud8888. ''Y888k/'*P '8888k 8888X ''*8h. us888u. -*8888888 ~'8888 ^8888 ='8888f8888r us888u. 888R .@88u .@^%8888' | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | 88888 |88888. 888R I888> beWE '888L :888'8888. Y888L '8888 X888X .xH8 .@88 '8888' 8888 8888 888R 4888>'88' .@88 '8888' 888R ''888E' x88: ')8b. | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | 88888 %88888 888R I888> 888E 888E d888 '88%' 8888 '8' X888|:888X 9888 9888 8888 8888 888R 4888> ' 9888 9888 888R 888E 8888N=*8888 | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | 88888 '> '8888> 888R I888> 888E 888E 8888.+' '888N =~' X888 X888X 9888 9888 8888 8888 888R 4888> 9888 9888 888R 888E %8' R88 | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | '8888L % |888 | u8888cJ888 888E 888F 8888L .u./'888& :h. X8*' |888X 9888 9888 .8888Lu= 8888 ,888B . .d888L .+ 9888 9888 888R 888E @8Wou 9% | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | '8888 '-*'' / '*888*P' .888N..888 '8888c. .+ d888' Y888*' X888xX' '8888..: 9888 9888 ^%888* '8888Y 8888' ^'8888*' 9888 9888 .888B . 888& .888888P' | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | '888. :' 'Y' ''888*'' '88888% ' 'Y Y' :~'888f '*888*' '888*''888' 'Y' 'Y' 'YP 'Y' '888*''888' ^*888% R888' ' ^'F | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | '''***~'' '' 'YP' '' ''' ^Y' ^Y' ^Y' ^Y' '% '' | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " | | | | ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " |___| |___| ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " (_____)---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------(_____) ");
+
+ System.out.println(bold + yellow);
+ for (int i = 0; i < titleString.size(); i++)
+ System.out.println(titleString.get(i));
+ System.out.println(white);
+ }
+
+ /**
+ * Prints the small title.
+ *
+ * @param x The starting x coordinate
+ * @param y The starting y coordinate
+ */
+ private void printLittleTitle(int x, int y) {
+ String white = "\033[0m", yellow = parser.getRightColor(Symbol.INKWELL), bold = "\033[1m";
+ List<String> titleString = new ArrayList<>();
+
+ String prefix = setPosition(x, y);
+ titleString.add(prefix
+ + " ______ __ _ __ __ __ _ ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " / ____/ ____ ____/ / ___ _ __ / | / / ____ _ / /_ __ __ _____ ____ _ / / (_) _____ ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " / / / __ \\ / __ / / _ \\ | |/_/ / |/ / / __ / / __/ / / / / / ___/ / __ / / / / / / ___/ ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " / /___ / /_/ / / /_/ / / __/ _> < / /| / / /_/ / / /_ / /_/ / / / / /_/ / / / / / (__ ) ");
+ prefix = setPosition(x, ++y);
+ titleString.add(prefix
+ + " \\____/ \\____/ \\__,_/ \\___/ /_/|_| /_/ |_/ \\__,_/ \\__/ \\__,_/ /_/ \\__,_/ /_/ /_/ /____/ ");
+
+ System.out.println(bold + yellow);
+ for (int i = 0; i < titleString.size(); i++)
+ System.out.println(titleString.get(i));
+ System.out.println(white);
+ }
+
+
+ /**
+ * Shows a deck and its "shadow", used to clearify what draw sources are decks.
+ *
+ * @param coord The coordinates where the deck should be printed
+ * @param reign The deck's top card's reign
+ * @param deckType Whether the deck is resource or gold
+ */
+ private void printDeck(Pair<Integer, Integer> coord, Symbol reign, DrawSource deckType) {
+ if (reign == null)
+ return;
+ String bianco = "\033[0m";
+ int xx = 8, yy = 7 + 1;
+
+ List<String> underCover = new ArrayList<>();
+ int x = coord.first(), y = coord.second();
+
+ String prefix = setPosition(x, y);
+ underCover.add(prefix + "┌────────────────┐");
+ prefix = setPosition(x, ++y);
+ underCover.add(prefix + "│ │");
+ prefix = setPosition(x, ++y);
+ underCover.add(prefix + "│ │");
+ prefix = setPosition(x, ++y);
+ underCover.add(prefix + "│ │");
+ prefix = setPosition(x, ++y);
+ underCover.add(prefix + "│ │");
+ prefix = setPosition(x, ++y);
+ underCover.add(prefix + "└────────────────┘");
+
+ System.out.println(bianco);
+ for (String s : underCover)
+ System.out.println(s);
+
+ x = coord.first() + 2;
+ y = coord.second() - 1;
+ String topDeckCar = this.parser.getGenericBack(reign, new Pair<>(x, y));
+ System.out.println(topDeckCar);
+
+ if (deckType == DrawSource.GOLDS_DECK)
+ System.out.println(setPosition(x + xx, y - 1 - 1) + getCardIndex(deckType));
+ if (deckType == DrawSource.RESOURCES_DECK)
+ System.out.println(setPosition(x + xx, y + yy + 1) + getCardIndex(deckType));
+
+ }
+
+ /**
+ * Prints the visible cards from which it's possible to draw.
+ *
+ * @param coord The coordinates from where the printing should be started
+ * @param visiblePlayableCards The drawable cards
+ */
+ private void printVisibleCards(Pair<Integer, Integer> coord,
+ Map<DrawSource, PlayableCard> visiblePlayableCards) {
+ String white = "\033[0m";
+
+ // obtain card obj
+ PlayableCard firstG = visiblePlayableCards.get(DrawSource.FIRST_VISIBLE),
+ secondG = visiblePlayableCards.get(DrawSource.SECOND_VISIBLE),
+ thirdR = visiblePlayableCards.get(DrawSource.THIRD_VISIBLE),
+ fourthR = visiblePlayableCards.get(DrawSource.FOURTH_VISIBLE);
+
+ // obtain card coord
+ int x = coord.first(), y = coord.second();
+ int xOffset = 18 + 2, yOffset = 6 + 2;
+ Pair<Integer, Integer> firstCoord = new Pair<>(x, y),
+ secondCoord = new Pair<>(x + xOffset, y), thirdCoord = new Pair<>(x, y + yOffset),
+ fourthCoord = new Pair<>(x + xOffset, y + yOffset);
+
+ // obtain card as string (if it exists)
+ try {
+ String firstToPrint;
+ String secondToPrint;
+ String thirdToPrint;
+ String fourthToPrint;
+ int xx = 8, yy = 6;
+
+ if (firstG != null) {
+ firstToPrint = this.parser.parseCard(firstG, firstCoord, null, true);
+ System.out.println(firstToPrint);
+ System.out.println(setPosition(firstCoord.first() + xx, firstCoord.second() - 1 - 2)
+ + getCardIndex(DrawSource.FIRST_VISIBLE));
+ System.out.println(white);
+ }
+ if (secondG != null) {
+ secondToPrint = this.parser.parseCard(secondG, secondCoord, null, true);
+ System.out.println(secondToPrint);
+ System.out
+ .println(setPosition(secondCoord.first() + xx, secondCoord.second() - 1 - 2)
+ + getCardIndex(DrawSource.SECOND_VISIBLE));
+ System.out.println(white);
+ }
+ if (thirdR != null) {
+ thirdToPrint = this.parser.parseCard(thirdR, thirdCoord, null, true);
+ System.out.println(thirdToPrint);
+ System.out.println(
+ setPosition(thirdCoord.first() + xx, thirdCoord.second() + yy + 1 + 1)
+ + getCardIndex(DrawSource.THIRD_VISIBLE));
+ System.out.println(white);
+ }
+ if (fourthR != null) {
+ fourthToPrint = this.parser.parseCard(fourthR, fourthCoord, null, true);
+ System.out.println(fourthToPrint);
+ System.out.println(
+ setPosition(fourthCoord.first() + xx, fourthCoord.second() + yy + 1 + 1)
+ + getCardIndex(DrawSource.FOURTH_VISIBLE));
+ System.out.println(white);
+ }
+
+ } catch (CardException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ /**
+ * Returns the index representing a draw source, in order to make easier for the user to choose
+ * the draw source.
+ *
+ * @param drawSource The draw source
+ *
+ * @return The corresponding index
+ */
+ private char getCardIndex(DrawSource drawSource) {
+ return switch (drawSource) {
+ case FIRST_VISIBLE -> '1';
+ case SECOND_VISIBLE -> '2';
+ case THIRD_VISIBLE -> '3';
+ case FOURTH_VISIBLE -> '4';
+ case GOLDS_DECK -> 'G';
+ case RESOURCES_DECK -> 'R';
+ default -> '0';
+ };
+ }
+
+ /**
+ * Prints a character a given amount of times.
+ *
+ * @param c The char
+ * @param times The times the char should be repeated
+ *
+ * @return String obtained concatenating the char
+ */
+ private String repeatChar(char c, int times) {
+
+ StringBuilder s = new StringBuilder();
+ for (int i = 0; i < times; i++)
+ s.append(c);
+ return s.toString();
+ }
+
+ /**
+ * Creates a box, used for centered messages
+ *
+ * @param totalWidth The width (column) of the content
+ * @param upperBorder The upper border
+ * @param middleBorder The vertical border
+ * @param lowerBorder The lower border
+ */
+ private void boxBuilder(int totalWidth, StringBuffer upperBorder, StringBuffer middleBorder,
+ StringBuffer lowerBorder) {
+ int lineWidth = totalWidth - 2;
+
+ // define the border template
+ upperBorder.append("╔");
+ upperBorder.append(repeatChar('═', lineWidth));
+ upperBorder.append("╗");
+
+ middleBorder.append("╠");
+ middleBorder.append(repeatChar('═', lineWidth));
+ middleBorder.append("╣");
+
+ lowerBorder.append("╚");
+ lowerBorder.append(repeatChar('═', lineWidth));
+ lowerBorder.append("╝");
+ }
+
+ private int getMaxElemLen(List<String> stringList) {
+ int max = 0;
+ for (String s : stringList) {
+ if (s.length() > max)
+ max = s.length();
+ }
+ return max;
+ }
+
+
+ /**
+ * Parses a certain coordinate to another, representing where a link number should be printed.
+ *
+ * @param coord The anchor card's coordinate
+ * @param corner The anchor card's corner
+ *
+ * @return The number's coordinates
+ */
+ private Pair<Integer, Integer> getNumberCoords(Pair<Integer, Integer> coord, Corner corner) {
+ return switch (corner) {
+ case Corner.TOP_LEFT -> new Pair<>(coord.first() - 2, coord.second() - 1);
+ case Corner.TOP_RIGHT -> new Pair<>(coord.first() + cardCols + 1, coord.second() - 1);
+ case Corner.BOTTOM_LEFT -> new Pair<>(coord.first() - 2, coord.second() + cardRows);
+ case Corner.BOTTOM_RIGHT -> new Pair<>(coord.first() + cardCols + 1,
+ coord.second() + cardRows);
+ default -> null;
+ };
+ }
+
+
+ /**
+ * Parses a certain coordinate to another, representing the coordinates of a hypotetical card
+ * that could be linked to the anchor's card.
+ *
+ * @param coord The anchor card's coordinates
+ * @param corner The anchor card's corner
+ *
+ * @return The hypotetical linked card
+ */
+ private Pair<Integer, Integer> getLinkedCoords(Pair<Integer, Integer> coord, Corner corner) {
+ return switch (corner) {
+ case Corner.TOP_LEFT -> new Pair<>(coord.first() + 1, coord.second() - 1);
+ case Corner.TOP_RIGHT -> new Pair<>(coord.first() - 1, coord.second() - 1);
+ case Corner.BOTTOM_LEFT -> new Pair<>(coord.first() + 1, coord.second() + 1);
+ case Corner.BOTTOM_RIGHT -> new Pair<>(coord.first() - 1, coord.second() + 1);
+ default -> null;
+ };
+ }
+
+ // ! PUBLIC METHODS //
+
+ /**
+ * Clears the terminal.
+ */
+ public void clearTerminal() {
+ System.out.println("\033[2J");
+ }
+
+ /**
+ * Prints the command prompt.
+ */
+ public void printPrompt(String customMessage) {
+ int termRows = this.getHeight();
+ if (customMessage == "") {
+ System.out.print(this.setPosition(1, termRows - infoLineOffset + 1));
+ } else {
+ System.out.print(
+ this.setPosition(1, termRows - infoLineOffset + 1) + customMessage + " ");
+ }
+ System.out.flush();
+ }
+
+
+ /**
+ * Prints a list of Strings to the terminal, first element of first line, last element on last
+ * line.
+ *
+ * @param messages List of messages to display
+ */
+ public void printMessages(List<String> messages) {
+ int termRows = this.getHeight();
+ Integer offset = 0;
+ for (String string : messages) {
+ System.out.println(this.setPosition(1, termRows - infoLineOffset - offset) + string);
+ offset++;
+ }
+ }
+
+
+ /**
+ * Prints a list of Strings to the terminal, first element on last line, last element of first
+ * line.
+ *
+ * @param message List of messages to display
+ */
+ public void printListReverse(List<String> message) {
+ int termRows = this.getHeight();
+ Integer offset = 0;
+ int size = message.size();
+ for (String string : message) {
+ System.out.println(
+ this.setPosition(1, termRows - infoLineOffset - size + offset + 1) + string);
+ offset++;
+ }
+ }
+
+ /**
+ * Prints a message in the line above the prompt.
+ *
+ * @param string The message to print
+ */
+ public void printMessage(String string) {
+ int termRows = this.getHeight();
+ System.out.println(this.setPosition(1, termRows - infoLineOffset) + string);
+ }
+
+ /**
+ * Prints all the players' available resources (from the board).
+ *
+ * @param availableResources map from the type of resource (Symbol) to its quantity (Integer)
+ * @param verticalOffset offset lines from the top (default is 1)
+ */
+ public void printAvailableResources(Map<Symbol, Integer> availableResources,
+ Integer verticalOffset) {
+ int vertCoord = this.getHeight() - this.infoLineOffset;
+ int termCols = this.getWidth();
+ String out = "";
+ String spaces = " ";
+ Integer len = availableResources.keySet().size() * (5 + spaces.length()); // icon, space, :,
+ // space, number
+ List<Symbol> toPrint = List.of(Symbol.PLANT, Symbol.INSECT, Symbol.FUNGUS, Symbol.ANIMAL,
+ Symbol.PARCHMENT, Symbol.FEATHER, Symbol.INKWELL);
+
+ for (Symbol resource : toPrint) {
+ out += parser.getRightColor(resource) + parser.getRightIcon(resource) + ": "
+ + availableResources.get(resource) + spaces;
+ }
+
+ System.out.println(this.setPosition((termCols - len) / 2, vertCoord) + out + "\033[0m");
+ }
+
+ /**
+ * Prints the whole board, including username, points and resources.
+ *
+ * @param username The username to print
+ * @param board the board to be printed
+ */
+ public void printPlayerBoard(String username, ClientBoard board) {
+ if (board == null) {
+ this.printMessage("No such player exists!");
+ return;
+ }
+ Map<Integer, ShownCard> placed = board.getPlaced();
+ for (Integer turn : placed.keySet()) {
+ this.printCard(placed.get(turn));
+ }
+ this.printAvailableResources(board.getAvailableResources(),
+ this.getHeight() - infoLineOffset);
+ this.printPoints(username, board.getColor(), board.getPoints());
+ }
+
+ /**
+ * Prints the hand of the player, which includes the 3 available-to-play cards.
+ *
+ * @param username username of the player
+ * @param color color of the player's token
+ * @param hand list of cards (as IDs)
+ */
+ public void printHand(String username, Color color, List<PlayableCard> hand) {
+ int termCols = this.getWidth();
+ Integer handSize = hand.size();
+ Integer spaces = 4;
+
+ Integer last = (termCols - (handSize) * (cardCols)) / 2 - spaces * (handSize - 1) / 2;
+ for (PlayableCard card : hand) {
+ try {
+ System.out.println(
+ parser.parseCard(card, new Pair<Integer, Integer>(last, 2), null, true)
+ + "\033[0m");
+ last += cardCols + spaces;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Prints the objectives, both common and secret, of a given player.
+ *
+ * @param username username of the player
+ * @param color color of the player's token
+ * @param secret secret objective (as ID)
+ * @param visibles array of common objectives (as IDs)
+ */
+ public void printObjectives(String username, Color color, Objective secret,
+ Pair<Objective, Objective> visibles) {
+ int termCols = this.getWidth();
+ Integer strlen;
+
+ strlen = ("Your secret objective").length();
+ username = this.parseUsername("Your", color) + " secret objective:";
+ Integer last = (termCols - strlen) / 2;
+ System.out.println(this.setPosition(last, 1) + username);
+
+ last = (termCols - cardCols) / 2;
+ System.out.println(
+ parser.parseObjective(secret, new Pair<Integer, Integer>(last, 2)) + "\033[0m");
+
+ int verticalSpaceAlreadyUsedForSecretObjective = (7) + 1 + 1;
+ printObjectivePair("Common objectives:", visibles,
+ verticalSpaceAlreadyUsedForSecretObjective);
+
+ }
+
+ /**
+ * Prints a pair of objectives, with a brief description above them.
+ *
+ * @param pairObjectives pair of objectives
+ * @param heightOffset offset lines from the top (default is 1)
+ */
+ public void printObjectivePair(String message, Pair<Objective, Objective> pairObjectives,
+ int heightOffset) {
+ int yOffset = (heightOffset <= 0) ? 1 : heightOffset;
+
+ // common objectives STRING
+ int xCoord = getDimStart(this.terminal.getWidth(), message.length());
+ message = setPosition(xCoord, yOffset++) + message;
+ System.out.println(message);
+
+ // common objectives CARDS
+ int cardWidth = 18, spaceBetweenSides = 4;
+ xCoord = getDimStart(this.getWidth(), (2 * cardWidth) + spaceBetweenSides);
+
+ Pair<Integer, Integer> obj1Coord = new Pair<>(xCoord, yOffset);
+ Pair<Integer, Integer> obj2Coord =
+ new Pair<>(xCoord + cardWidth + spaceBetweenSides, yOffset);
+ String obj1 = this.parser.parseObjective(pairObjectives.first(), obj1Coord);
+ String obj2 = this.parser.parseObjective(pairObjectives.second(), obj2Coord);
+
+ System.out.println(obj1 + obj2);
+ }
+
+ /**
+ * Prints the message history of the most recent messages.
+ *
+ * @param chat chat object, as a list of strings
+ */
+ public void printChat(List<String> chat) {
+ printSimpleList(chat, false, false);
+ }
+
+ /**
+ * Prints the welcome screen in the middle of the tui view.
+ */
+ public void printWelcomeScreen() {
+
+ // get title and welcome sizes
+ int welcomeHeight = 5, welcomeWidth = 88 + 2; // width must be even (pari)
+ int spaceBetween = 3;
+ int titleHeight = 21, titleWidth = 210 + 2; // width must be even (pari)
+ int verticalOffset = -10;
+
+ boolean isLittle = (this.terminal.getWidth() < titleWidth
+ || this.terminal.getHeight() < welcomeHeight - verticalOffset + titleHeight) ? true
+ : false;
+ if (isLittle) {
+ titleHeight = 5;
+ titleWidth = 118 + 2;
+ verticalOffset = -1;
+ }
+
+ // get coordinates
+ int welcomeStartY =
+ getDimStart(this.getHeight(), welcomeHeight + spaceBetween + titleHeight);
+ int titleStartY = welcomeStartY + welcomeHeight + spaceBetween;
+
+ int welcomeStartX = getDimStart(this.getWidth(), welcomeWidth);
+ int titleStartX = getDimStart(this.getWidth(), titleWidth);
+
+ // print welcome and title
+ printWelcome(welcomeStartX, welcomeStartY + verticalOffset);
+ printTitle(titleStartX, titleStartY + verticalOffset, isLittle);
+ }
+
+ /**
+ * Prints the game's ranking.
+ *
+ * @param ranking The Leaderboard
+ * @param username The player's username, used to highlight your rank
+ */
+ public synchronized void printEndScreen(List<LeaderboardEntry> ranking, String username) {
+ int maxWidth = this.getWidth() - 2;
+
+ this.clearTerminal();
+ List<String> winning = new ArrayList<String>();
+ List<String> losing = new ArrayList<String>();
+ for (LeaderboardEntry leaderboardEntry : ranking) {
+ String user = leaderboardEntry.username();
+ if (user.equals(username)) {
+ String color;
+ if (leaderboardEntry.winner()) {
+ color = "\033[31m";
+ } else {
+ color = "\033[33m";
+ }
+
+ user = color + "YOU" + "\033[0m";
+ }
+ String entry = user + " (" + leaderboardEntry.points() + ")";
+ if (leaderboardEntry.winner()) {
+ winning.add(entry);
+ } else {
+ losing.add(entry);
+ }
+ }
+
+ String winningTitle = winning.size() == 1 ? "Winner: " : "Draw:";
+ String losingTitle = "Losers:";
+
+ final int startPos = (maxWidth - winningTitle.length()) / 2;
+ System.out.println(this.setPosition(startPos, 2) + winningTitle);
+ int i = 0;
+ final int base = 3;
+ for (String winner : winning) {
+ System.out.println(this.setPosition(startPos, base + i) + winner);
+ i++;
+ }
+
+ if (losing.size() > 0) {
+ i++;
+ System.out.println(this.setPosition(startPos, base + i) + losingTitle);
+ i++;
+ for (String loser : losing) {
+ System.out.println(this.setPosition(startPos, base + i) + loser);
+ i++;
+ }
+ }
+
+ System.out.println(this.setPosition(0, this.getHeight()) + " ");
+ System.exit(0);
+ }
+
+ /**
+ * Prints the specified initial card front and back in the middle of the screen.
+ *
+ * @param initialCard initial card to print
+ * @param heightOffset offset lines from the top (default is 1)
+ */
+ public void printInitialSideBySide(InitialCard initialCard, int heightOffset) {
+
+ int offset = (heightOffset <= 0) ? 1 : heightOffset;
+ String faceup, facedown;
+ int cardWidth = 18, spaceBetweenSides = 4;
+ int width = getDimStart(this.getWidth(), (2 * cardWidth) + spaceBetweenSides);
+
+ Pair<Integer, Integer> faceupCoord = new Pair<>(width, offset);
+ Pair<Integer, Integer> facedownCoord =
+ new Pair<>(width + cardWidth + spaceBetweenSides, offset);
+
+ try {
+ faceup = this.parser.parseCard(initialCard, faceupCoord, null, true);
+ facedown = this.parser.parseCard(initialCard, facedownCoord, null, false);
+ System.out.println(faceup + facedown);
+ } catch (CardException e) {
+ }
+ }
+
+ /**
+ * Prints the specified initial card front and back in the middle of the screen.
+ *
+ * @param playableCard gold/resource card to print
+ * @param heightOffset offset lines from the top. For default (y-centered) must be 0
+ */
+ public void printPlayableFrontAndBack(PlayableCard playableCard, int heightOffset) {
+ int yCoord = (heightOffset > 0) ? heightOffset : getDimStart(getHeight() - 1, 6);
+ int cardWidth = 18, spaceBetweenSides = 4;
+ int xCoord = getDimStart(getWidth(), cardWidth + spaceBetweenSides + cardWidth);
+
+ String faceUp, faceDown, bianco = "\033[0m";
+ Pair<Integer, Integer> faceupCoord = new Pair<>(xCoord, yCoord);
+ Pair<Integer, Integer> facedownCoord =
+ new Pair<>(xCoord + cardWidth + spaceBetweenSides, yCoord);
+
+ try {
+ faceUp = this.parser.parseCard(playableCard, faceupCoord, null, true);
+ faceDown = this.parser.parseCard(playableCard, facedownCoord, null, false);
+ System.out.println(faceUp + faceDown + bianco);
+ } catch (CardException e) {
+ }
+ }
+
+ /**
+ * Prints a one-line message in the center of the screen.
+ *
+ * @param message message to display
+ * @param heightOffset offset lines from the top. For default (y-centered) must be 0
+ */
+ public void printCenteredMessage(String message, int heightOffset) {
+ int maxWidth = message.length();
+ if (maxWidth > getWidth() - 4)
+ return;
+
+ String resetStyle = "\033[0m", style = "\033[1m";
+
+ // int yCoord = getDimStart(this.terminal.getHeight(), 3);
+ int yCoord = (heightOffset > 0) ? heightOffset : getDimStart(this.terminal.getHeight(), 3);
+ int xCoord = getDimStart(this.terminal.getWidth(), maxWidth + 4);
+ String prefix = setPosition(xCoord, yCoord);
+
+ // define the border template
+ StringBuffer upperBorder = new StringBuffer("╔");
+ upperBorder.append(repeatChar('═', maxWidth + 2));
+ upperBorder.append("╗");
+ StringBuffer center = new StringBuffer("║ ");
+ center.append(style + message + resetStyle);
+ center.append(" ║");
+ StringBuffer lowerBorder = new StringBuffer("╚");
+ lowerBorder.append(repeatChar('═', maxWidth + 2));
+ lowerBorder.append("╝");
+
+ // print
+ System.out.println(prefix + upperBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + center.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + lowerBorder.toString());
+
+ }
+
+ /**
+ * Prints the drawing screen, containing the 2 decks and the 4 visible cards. Unavailable
+ * resources are not displayed.
+ *
+ * @param decksTopReign pair of the 2 top-deck cards
+ * @param visiblePlayableCards map of visible cards
+ */
+ public void printDrawingScreen(Pair<Symbol, Symbol> decksTopReign,
+ Map<DrawSource, PlayableCard> visiblePlayableCards) {
+ // int maxHeight = this.getHeight() - this.infoLineOffset;
+ int deckHeight = 6 + 2, deckWidth = 18 + 1; // width must be even (pari)
+ int xSpaceBetween = 12, ySpaceBetween = 0;
+ int cardsHeight = 6 + 2, cardsWidth = 18 + 2 + 18; // width must be even (pari)
+
+ int deckStartX = getDimStart(this.getWidth(), deckWidth + xSpaceBetween + cardsWidth);
+ int deckStartY = getDimStart(this.getHeight(), deckHeight + ySpaceBetween + cardsHeight);
+ int cardsStartX = deckStartX + deckWidth + xSpaceBetween;
+ int cardsStartY = deckStartY;
+
+ printDeck(new Pair<>(deckStartX, deckStartY), decksTopReign.first(), DrawSource.GOLDS_DECK);
+ printDeck(new Pair<>(deckStartX, deckStartY + deckHeight + ySpaceBetween),
+ decksTopReign.second(), DrawSource.RESOURCES_DECK);
+ printVisibleCards(new Pair<>(cardsStartX, cardsStartY), visiblePlayableCards);
+ }
+
+ /**
+ * Prints the list of matches (joinable or not) in the center of the screen. It can print a
+ * maximum of 99 matches.
+ *
+ * @param joinableMatches list of available matches
+ * @param unavailableMatches list of not joinable matches
+ * @param heightOffset offset lines from the top (default is 1)
+ */
+ public void printMatchesLobby(List<AvailableMatch> joinableMatches,
+ List<AvailableMatch> unavailableMatches, int heightOffset) {
+ int yCoord = (heightOffset > 0) ? heightOffset : 1;
+ int maxWidth = 45;
+ int xCoord = getDimStart(getWidth(), maxWidth);
+ String prefix = setPosition(xCoord, yCoord);
+
+ // define the border template
+ StringBuffer upperBorder = new StringBuffer();
+ StringBuffer middleBorder = new StringBuffer();
+ StringBuffer lowerBorder = new StringBuffer();
+ boxBuilder(maxWidth, upperBorder, middleBorder, lowerBorder);
+
+ // print upper and middle border
+ System.out.println(prefix + upperBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + "║ \033[1mMatches Slots\033[0m ║"); // manually
+ // adjust
+ // according
+ // to
+ // maxWidth
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + middleBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+
+
+ // print list of joinable matches
+ String white = "\033[0m", green = "\033[32m", yellow = "\033[33m";
+ int matchIndex = 1;
+ for (AvailableMatch m1 : joinableMatches) {
+
+ if (joinableMatches.get(matchIndex - 1).isRejoinable()) {
+ System.out.printf("%s║ %s[%02d] %-31s %s ║", prefix, yellow, matchIndex,
+ m1.name().toString(), white); // manually adjust according to maxWidth
+ } else {
+ System.out.printf("%s║ %s[%02d] %-31s %s/%s%s ║", prefix, green, matchIndex,
+ m1.name().toString(), m1.currentPlayers().toString(),
+ m1.maxPlayers().toString(), white); // manually adjust according to maxWidth
+ }
+
+ prefix = setPosition(xCoord, ++yCoord);
+ matchIndex++;
+ }
+
+ // print list of unavailable matches
+ String red = "\033[31m";
+ for (AvailableMatch m2 : unavailableMatches) {
+
+ System.out.printf("%s║ [--] %s%-31s %s/%s%s ║", prefix, red, m2.name().toString(),
+ m2.currentPlayers().toString(), m2.maxPlayers().toString(), white); // manually
+ // adjust
+ // according
+ // to
+ // maxWidth
+ prefix = setPosition(xCoord, ++yCoord);
+ }
+
+ // print lower border
+ System.out.print(prefix + lowerBorder.toString());
+ }
+
+ /**
+ * Prints the scoreboard at the end of the match.
+ *
+ * @param playerToPoints map from player name as string, to total points as integer
+ * @param heightOffset offset lines from the top (default is 1)
+ */
+ public void printScoreboard(Map<String, Integer> playerToPoints, int heightOffset) {
+ int yCoord = (heightOffset > 0) ? heightOffset : 1;
+ int maxWidth = 38 + 2;
+ int xCoord = getDimStart(getWidth(), maxWidth);
+ String prefix = setPosition(xCoord, yCoord);
+
+ // define the border template
+ StringBuffer upperBorder = new StringBuffer("╔");
+ upperBorder.append(repeatChar('═', maxWidth - 2));
+ upperBorder.append("╗");
+ StringBuffer middleBorder = new StringBuffer("╠");
+ middleBorder.append(repeatChar('═', maxWidth - 2));
+ middleBorder.append("╣");
+ StringBuffer lowerBorder = new StringBuffer("╚");
+ lowerBorder.append(repeatChar('═', maxWidth - 2));
+ lowerBorder.append("╝");
+
+ // print upper and middle border
+ System.out.println(prefix + upperBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + "║ \033[1mPlayer Score\033[0m ║"); // manually
+ // adjust
+ // according
+ // to
+ // maxWidth
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + middleBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+
+ // print list of players
+ for (String s : playerToPoints.keySet()) {
+ System.out.printf("%s║ %-31s%4s ║", prefix, s, playerToPoints.get(s).toString()); // manually
+ // adjust
+ // according
+ // to
+ // maxWidth
+ prefix = setPosition(xCoord, ++yCoord);
+ }
+
+ // print lower border
+ System.out.print(prefix + lowerBorder.toString());
+ }
+
+ /**
+ * Prints the hand of the player at the bottom of the screen.
+ *
+ * @param hand list of the 3 playable cards (hand)
+ */
+ public void printHandAtBottom(List<PlayableCard> hand) {
+ int termCols = this.getWidth();
+ Integer handSize = hand.size();
+ Integer spaces = 4;
+
+ Integer last = (termCols - (handSize) * (cardCols)) / 2 - spaces * (handSize - 1) / 2;
+ Integer row = this.getHeight() - TuiPrinter.cardRows - 3;
+
+ for (PlayableCard card : hand) {
+ try {
+ System.out.println(
+ parser.parseCard(card, new Pair<Integer, Integer>(last, row), null, true)
+ + "\033[0m");
+ last += cardCols + spaces;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Prints a list of strings with a box around them.
+ *
+ * @param stringList list of strings
+ * @param msg message to display
+ * @param textColor color of the players' name (red or green)
+ * @param isCentered if the list has to be centered in the tui or not
+ */
+ public void printStringsBoxed(List<String> stringList, String msg, Color textColor,
+ Boolean isCentered) {
+ int maxWidth = 40; // leave it even
+ int xCoord = getDimStart(getWidth(), maxWidth);
+ int yCoord = (isCentered) ? getDimStart(terminal.getHeight(), stringList.size() + 4) : 1;
+ String bold = "\033[1m", reset = "\033[0m", color;
+ switch (textColor) {
+ case RED:
+ color = "\033[31m";
+ break;
+ case GREEN:
+ color = "\033[32m";
+ break;
+ default:
+ color = reset;
+ break;
+ }
+
+ // define the border template
+ StringBuffer upperBorder = new StringBuffer();
+ StringBuffer middleBorder = new StringBuffer();
+ StringBuffer lowerBorder = new StringBuffer();
+ boxBuilder(maxWidth, upperBorder, middleBorder, lowerBorder);
+
+ // define title of the table
+ // String msg = "Connected Players";
+ StringBuffer title = new StringBuffer();
+ title.append("║").append(bold);
+ if (msg.length() % 2 != 0) {
+ title.append(repeatChar(' ', (maxWidth - msg.length() - 2) / 2));
+ title.append(msg);
+ title.append(repeatChar(' ', ((maxWidth - msg.length() - 2) / 2) + 1));
+
+ } else {
+ title.append(repeatChar(' ', (maxWidth - msg.length() - 2) / 2));
+ title.append(msg);
+ title.append(repeatChar(' ', (maxWidth - msg.length() - 2) / 2));
+ }
+ title.append("║").append(reset);
+
+ // define players' lines
+ List<String> playerList = new ArrayList<>();
+ for (String s : stringList) {
+
+ StringBuffer player = new StringBuffer();
+ player.append("║").append(color);
+ if (s.length() % 2 != 0) {
+ player.append(repeatChar(' ', (maxWidth - s.length() - 2) / 2));
+ player.append(s);
+ player.append(repeatChar(' ', ((maxWidth - s.length() - 2) / 2) + 1));
+
+ } else {
+ player.append(repeatChar(' ', (maxWidth - s.length() - 2) / 2));
+ player.append(s);
+ player.append(repeatChar(' ', (maxWidth - s.length() - 2) / 2));
+ }
+ player.append(reset).append("║");
+
+ playerList.add(player.toString());
+ }
+
+ // print it all
+ String prefix = setPosition(xCoord, yCoord);
+ System.out.println(prefix + upperBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + title.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ System.out.println(prefix + middleBorder.toString());
+ prefix = setPosition(xCoord, ++yCoord);
+ for (String s : playerList) {
+ System.out.println(prefix + s);
+ prefix = setPosition(xCoord, ++yCoord);
+ }
+ System.out.println(prefix + lowerBorder);
+
+ }
+
+ /**
+ * Prints a list, simple or numbered, either in the center or in the bottom left of the tui.
+ *
+ * @param stringList list of strings
+ * @param isCentered if you want it centered or not
+ * @param isNumbered if you want it numbered or not
+ */
+ public void printSimpleList(List<String> stringList, Boolean isCentered, Boolean isNumbered) {
+ // coords with case
+ int xCoord = (isCentered) ? getDimStart(getWidth(), getMaxElemLen(stringList)) : 1;
+ int yCoord = (isCentered) ? (terminal.getHeight() - stringList.size())
+ : (this.getHeight() - infoLineOffset + 1 - stringList.size());
+ int index = 0;
+ if (yCoord <= 0) {
+ index = -yCoord + 2;
+ yCoord = 1;
+ }
+
+ // printing phase
+ int i = 1;
+ String prefix = setPosition(xCoord, yCoord);
+ for (; index < stringList.size(); index++) {
+ if (isNumbered)
+ System.out.println(prefix + String.valueOf(i++) + ") " + stringList.get(index));
+ else
+ System.out.println(prefix + stringList.get(index));
+
+ prefix = setPosition(xCoord, ++yCoord);
+ }
+ }
+
+ /**
+ * Prints indexes for available spots, during the placing card phase.
+ *
+ * @param validPlaces map FROM board's coordinates where the hypotetic card would be TO a pair,
+ * consisting of a number (the index) and the corner where future card would be linked
+ */
+ public void printValidPlaces(Map<Pair<Integer, Integer>, Pair<Integer, Corner>> validPlaces) {
+ Corner link;
+ Pair<Integer, Integer> abs;
+ Pair<Integer, Integer> linkedCard;
+ for (Pair<Integer, Integer> coord : validPlaces.keySet()) {
+ link = validPlaces.get(coord).second();
+ linkedCard = this.getLinkedCoords(coord, link);
+ abs = this.getNumberCoords(this.getAbsoluteCoords(linkedCard), link);
+
+ System.out.print(
+ this.setPosition(abs.first(), abs.second()) + validPlaces.get(coord).first());
+ System.out.flush();
+ }
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.html
new file mode 100644
index 00000000..1c7df47b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.html
@@ -0,0 +1 @@
+ValidPositions
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.java.html
new file mode 100644
index 00000000..b84fd8fb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/ValidPositions.java.html
@@ -0,0 +1,97 @@
+ValidPositions.java
package it.polimi.ingsw.client.frontend.tui;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import it.polimi.ingsw.client.frontend.ShownCard;
+import it.polimi.ingsw.exceptions.CardException;
+import it.polimi.ingsw.gamemodel.CardFace;
+import it.polimi.ingsw.gamemodel.Corner;
+import it.polimi.ingsw.gamemodel.Symbol;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * Valid positions of a board, ie all the points a new card can be linked to
+ */
+public class ValidPositions {
+ private final Map<Pair<Integer, Integer>, BoardPosition> coordinates;
+ private static final Map<Corner, Pair<Integer, Integer>> offsets =
+ Map.of(Corner.TOP_LEFT, new Pair<Integer, Integer>(-1, 1), Corner.TOP_RIGHT, new Pair<Integer, Integer>(1, 1),
+ Corner.BOTTOM_LEFT, new Pair<Integer, Integer>(-1, -1), Corner.BOTTOM_RIGHT, new Pair<Integer, Integer>(1, -1));
+
+
+ /**
+ * Class constructor.
+ */
+ public ValidPositions() {
+ this.coordinates = new HashMap<>();
+ }
+
+ /**
+ * Sums two coordinates.
+ *
+ * @param coord First coordinate
+ * @param offset Second coordinate
+ *
+ * @return The sum of the two coordinates
+ */
+ private Pair<Integer, Integer> offsetCoord(Pair<Integer, Integer> coord, Pair<Integer, Integer> offset) {
+ return new Pair<Integer, Integer>(coord.first() + offset.first(), coord.second() + offset.second());
+ }
+
+
+ /**
+ * Checks if a coordinate is a valid link point.
+ *
+ * @param coord The coordinate to check
+ *
+ * @return whether the coordinate is a valid link point or not
+ */
+ public boolean isValid(Pair<Integer, Integer> coord) {
+ if (this.coordinates.get(coord) == null) {
+ return false;
+ }
+
+ return this.coordinates.get(coord).isValid();
+ }
+
+ /**
+ * Gets all the valid linking points.
+ *
+ * @return A map from coordinate to anchor's corner and linking point's index
+ */
+ public Map<Pair<Integer, Integer>, Pair<Integer, Corner>> getValidPlaces() {
+ Map<Pair<Integer, Integer>, Pair<Integer, Corner>> valids = new HashMap<>();
+ int pos = 1;
+ for (Pair<Integer, Integer> coord : this.coordinates.keySet()) {
+ if (this.coordinates.get(coord).isValid()) {
+ valids.put(coord, new Pair<Integer,Corner>(pos, this.coordinates.get(coord).link().get()));
+ pos++;
+ }
+ }
+
+ return valids;
+ }
+
+ /**
+ * Adds a card, updating all the valid linking points.
+ *
+ * @param card The added card
+ */
+ public void addCard(ShownCard card) {
+ CardFace cardFace = card.card().getSide(card.side());
+ Pair<Integer, Integer> coord = card.coords();
+ this.coordinates.put(coord, new BoardPosition(false, Optional.empty()));
+ for (Corner corner : offsets.keySet()) {
+ try {
+ if (!cardFace.getCorner(corner).equals(Symbol.EMPTY_CORNER)) {
+ this.coordinates.putIfAbsent(this.offsetCoord(coord, offsets.get(corner)), new BoardPosition(true, Optional.of(corner)));
+ } else {
+ this.coordinates.put(this.offsetCoord(coord, offsets.get(corner)), new BoardPosition(false, Optional.empty()));
+ }
+ } catch (CardException e) {
+ }
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.html
new file mode 100644
index 00000000..5ef4e1ee
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.tui
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.source.html
new file mode 100644
index 00000000..6eb74bee
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend.tui/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend.tui
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.html
new file mode 100644
index 00000000..7a73c2a9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.html
@@ -0,0 +1 @@
+ClientBoard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.java.html
new file mode 100644
index 00000000..5184de6d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/ClientBoard.java.html
@@ -0,0 +1,158 @@
+ClientBoard.java
package it.polimi.ingsw.client.frontend;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * This class contains just elements needed to show the player's board, points, resources, hand and objectives
+ */
+public class ClientBoard {
+ private final Map<Integer, ShownCard> placed;
+ private Integer placementNumber;
+ private List<PlayableCard> hand;
+ private Integer points;
+ private Map<Symbol, Integer> availableResources;
+ private final Color color;
+ private Objective objective;
+ private InitialCard initialCard;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param color The player pawn's color
+ * @param hand The player's hand
+ */
+ public ClientBoard(Color color, List<PlayableCard> hand) {
+ this.placementNumber = 0;
+ this.placed = new HashMap<>();
+ this.color = color;
+
+ this.hand = new ArrayList<>();
+ this.hand = hand;
+
+ this.points = 0;
+
+ this.availableResources = new HashMap<>();
+ Symbol.getBasicResources().forEach((reign -> this.availableResources.put(reign, 0)));
+ }
+
+ /**
+ * Sets the secret objective.
+ *
+ * @param objective The chosen secret objective
+ */
+ public void setSecretObjective(Objective objective) {
+ this.objective = objective;
+ }
+
+ /**
+ * Adds a card to the player's board.
+ *
+ * @param coords The card's coordinates
+ * @param card The chosen card
+ * @param side The chosen side
+ * @param points The player's point (total)
+ * @param resources The player's resources (total)
+ */
+ public void placeCard(Pair<Integer, Integer> coords, PlayableCard card, Side side, Integer points, Map<Symbol, Integer> resources) {
+ this.hand.remove(card);
+ this.placed.put(placementNumber, new ShownCard(card, side, coords));
+ this.points = points;
+ this.availableResources = resources;
+ this.placementNumber++;
+ }
+
+ /**
+ * Adds a card to the player's hand.
+ *
+ * @param card The drawn card
+ */
+ public void drawCard(PlayableCard card) {
+ this.hand.add(card);
+ }
+
+
+ /**
+ * Sets the initial card. This still does not put it in the board, as the side is still not chosen.
+ *
+ * @param card The chosen card
+ *
+ * @see ClientBoard#placeInitial(Side, Map)
+ */
+ public void setInitial(InitialCard card) {
+ this.initialCard = card;
+ }
+
+ /**
+ * Places initial card on the board. At this point, the initial card's side has been chosen
+ *
+ * @param side The chosen side
+ * @param availableResources The player's resources (total)
+ */
+ public void placeInitial(Side side, Map<Symbol, Integer> availableResources) {
+ this.placed.put(placementNumber, new ShownCard(this.initialCard, side, new Pair<>(0, 0)));
+ this.availableResources = availableResources;
+ this.placementNumber++;
+ }
+
+ /**
+ * @return The initial card.
+ */
+ public InitialCard getInitialCard() {return this.initialCard;}
+
+ /**
+ * @return The card's index (ie, the first card played would have a placementNumber of 1).
+ */
+ public Integer getPlacementNumber() {
+ return placementNumber;
+ }
+
+ /**
+ * @return The player's hand.
+ */
+ public List<PlayableCard> getHand() {
+ return hand;
+ }
+
+ /**
+ * @return The player's points.
+ */
+ public Integer getPoints() {
+ return points;
+ }
+
+ /**
+ * @return The player's board.
+ */
+ public Map<Integer, ShownCard> getPlaced() {
+ return placed;
+ }
+
+ /**
+ * @return The player's resources.
+ */
+ public Map<Symbol, Integer> getAvailableResources() {
+ return availableResources;
+ }
+
+ /**
+ * @return The player pawn's color.
+ */
+ public Color getColor() {
+ return color;
+ }
+
+ /**
+ * @return The player's secret objective.
+ */
+ public Objective getObjective() {
+ return objective;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.html
new file mode 100644
index 00000000..01594a3e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.html
@@ -0,0 +1 @@
+GraphicalView
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.java.html
new file mode 100644
index 00000000..b8e305bb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/GraphicalView.java.html
@@ -0,0 +1,553 @@
+GraphicalView.java
package it.polimi.ingsw.client.frontend;
+
+import java.util.*;
+import it.polimi.ingsw.client.network.NetworkHandler;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.RequestStatus;
+
+/**
+ * Class to manage graphical clients
+ */
+public abstract class GraphicalView {
+ protected NetworkHandler networkHandler;
+ protected Map<String, ClientBoard> clientBoards;
+ protected List<String> players; // ordered by turn
+ protected String currentPlayer;
+ protected Pair<Objective, Objective> visibleObjectives;
+ protected Map<DrawSource, PlayableCard> visiblePlayableCards;
+ protected Pair<Symbol, Symbol> decksTopReign;
+ protected boolean lastTurn = false;
+ protected List<AvailableMatch> availableMatches;
+ protected String username;
+ protected final LastRequest lastRequest;
+ private boolean matchStarted = false;
+ private final Boolean sync = true;
+
+ /**
+ * Class constructor.
+ */
+ public GraphicalView() {
+ this.lastRequest = new LastRequest();
+ this.lastRequest.setStatus(RequestStatus.PENDING);
+ }
+
+ /**
+ * Sets the username of the corresponding player.
+ *
+ * @param username The chosen username
+ */
+ protected void setUsername(String username) {
+ this.username = username;
+ this.networkHandler.setUsername(username);
+ }
+
+ /**
+ * @return Whether is the last turn or not.
+ */
+ public boolean isLastTurn() {
+ return this.lastTurn;
+ }
+
+ /**
+ * Sets the last request's status.
+ *
+ * @param status Last request's status
+ */
+ public void setLastRequestStatus(RequestStatus status) {
+ this.lastRequest.setStatus(status);
+ }
+
+ /**
+ * Sets the internal state according to the occurrence of an error.
+ *
+ * @param exception The thrown exception
+ */
+ public void notifyError(Exception exception) {
+ this.setLastRequestStatus(RequestStatus.FAILED);
+ }
+
+ /**
+ * Sets the network interface in order to communicate.
+ *
+ * @param networkHandler the interface to communicate
+ */
+ public void setNetworkHandler(NetworkHandler networkHandler) {
+ this.networkHandler = networkHandler;
+ }
+
+ /**
+ * Tries to create a match.
+ *
+ * @param maxPlayers maximum amount of players
+ * @param matchName The match's name
+ */
+ public void createMatch(String matchName, Integer maxPlayers) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.createMatch(matchName, maxPlayers);
+ }
+
+ /**
+ * Tries to join a match.
+ *
+ * @param matchName the match's name
+ */
+ public void joinMatch(String matchName) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.joinMatch(matchName);
+ }
+
+ /**
+ * Sends a broadcast text.
+ *
+ * @param text The content
+ */
+ public void sendBroadcastText(String text) {
+ this.networkHandler.sendBroadcastText(text);
+ }
+
+ /**
+ * Sends a private text.
+ *
+ * @param recipient The recipient
+ * @param text The content
+ */
+ public void sendPrivateText(String recipient, String text) {
+ this.networkHandler.sendPrivateText(recipient, text);
+ }
+
+ /**
+ * Draws an initial card for the player.
+ */
+ public void drawInitialCard() {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.drawInitialCard();
+ }
+
+ /**
+ * Communicates the chosen initial card side.
+ *
+ * @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
+ */
+ public void chooseInitialCardSide(Side side) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.chooseInitialCardSide(side);
+ }
+
+ /**
+ * Draws two secret objectives.
+ */
+ public void drawSecretObjectives() {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.drawSecretObjectives();
+ }
+
+ /**
+ * Communicates the chosen secret objective.
+ *
+ * @param objective The chosen objective
+ */
+ public void chooseSecretObjective(Objective objective) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.clientBoards.get(this.username).setSecretObjective(objective);
+ this.networkHandler.chooseSecretObjective(objective);
+ }
+
+ /**
+ * Plays a card.
+ *
+ * @param coords The coordinates on which to place the card
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ */
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ new Thread(() -> this.networkHandler.playCard(coords, card, side)).start();
+ }
+
+ /**
+ * Draws a card.
+ *
+ * @param source The drawing source to draw the card from
+ */
+ public void drawCard(DrawSource source) {
+ this.setLastRequestStatus(RequestStatus.PENDING);
+ this.networkHandler.drawCard(source);
+ }
+
+ /**
+ * Method used to show the turn has changed.
+ */
+ public abstract void changePlayer();
+
+ /**
+ * Goes to the next turn, making sure that the current player is set and that he plays the right
+ * turn (choose initial card/objective, or make a move).
+ */
+ private void nextPlayer() {
+ if (this.currentPlayer == null)
+ this.currentPlayer = this.players.get(0);
+ else
+ this.currentPlayer = this.players.get((this.players.indexOf(currentPlayer) + 1) % this.players.size());
+
+
+ if (this.currentPlayer.equals(this.username)) {
+ if (this.clientBoards.get(this.username).getPlaced().isEmpty())
+ this.drawInitialCard();
+ else if (this.clientBoards.get(this.username).getObjective() == null)
+ this.drawSecretObjectives();
+ else
+ this.makeMove();
+ } else {
+ this.changePlayer();
+ }
+ }
+
+
+ /**
+ * Ask the user to make a play. Must call {@link GraphicalView#playCard(Pair, PlayableCard, Side)}.
+ */
+ public abstract void makeMove();
+
+
+ /**
+ * Starts match on the client side, setting all variables to their initial values.
+ *
+ * @param playersUsernamesAndPawns Map containing all players' pawns, indexed by their username
+ * @param playersHands Map containing all the players' hands, indexed by their username
+ * @param visibleObjectives The two objectives common to every player
+ * @param visiblePlayableCards The four cards that can be drawn, visible to everyone
+ * @param decksTopReign the reigns of the two decks' top
+ */
+ public void matchStarted(Map<String, Color> playersUsernamesAndPawns, Map<String, List<PlayableCard>> playersHands,
+ Pair<Objective, Objective> visibleObjectives, Map<DrawSource, PlayableCard> visiblePlayableCards,
+ Pair<Symbol, Symbol> decksTopReign) {
+ this.setupMatch(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReign);
+ this.notifyMatchStarted();
+ this.nextPlayer();
+ }
+
+ /**
+ * Resumes match on the client side, setting all variables to their initial values.
+ *
+ * @param playersUsernamesAndPawns Map containing all players' pawns, indexed by their username
+ * @param playersHands Map containing all the players' hands, indexed by their username
+ * @param visibleObjectives The two objectives common to every player
+ * @param visiblePlayableCards The four cards that can be drawn, visible to everyone
+ * @param decksTopReign the reigns of the two decks' top
+ * @param secretObjective Secret objective of the current player
+ * @param availableResources Available resources of all the players
+ * @param placedCards Placed cards of all the players
+ * @param playerPoints Points of all the players
+ * @param currentPlayer The current player
+ * @param drawPhase If the match is resumed in draw phase
+ */
+ public void resumeMatch(Map<String, Color> playersUsernamesAndPawns, Map<String, List<PlayableCard>> playersHands,
+ Pair<Objective, Objective> visibleObjectives, Map<DrawSource, PlayableCard> visiblePlayableCards,
+ Pair<Symbol, Symbol> decksTopReign, Objective secretObjective, Map<String, Map<Symbol, Integer>> availableResources,
+ Map<String, Map<Pair<Integer, Integer>, PlacedCard>> placedCards, Map<String, Integer> playerPoints, String currentPlayer, boolean drawPhase) {
+ this.setupMatch(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReign);
+ this.clientBoards.get(username).setSecretObjective(secretObjective);
+ for (String player : placedCards.keySet()) {
+ ClientBoard clientBoard = this.clientBoards.get(player);
+ Map<Pair<Integer, Integer>, PlacedCard> playerBoard = placedCards.get(player);
+ List<Pair<Integer, Integer>> orderedCards = playerBoard.keySet().stream()
+ .sorted(Comparator.comparingInt(c -> playerBoard.get(c).getTurn()))
+ .toList();
+ for (Pair<Integer, Integer> cardCoord : orderedCards) {
+ if (cardCoord.equals(new Pair<>(0, 0))) {
+ clientBoard.setInitial((InitialCard) playerBoard.get(cardCoord).getCard());
+ clientBoard.placeInitial(playerBoard.get(cardCoord).getPlayedSide(),
+ availableResources.get(player));
+ } else {
+ clientBoard.placeCard(cardCoord,
+ (PlayableCard) playerBoard.get(cardCoord).getCard(),
+ playerBoard.get(cardCoord).getPlayedSide(),
+ playerPoints.get(player), availableResources.get(player)
+ );
+ }
+ }
+ }
+ this.currentPlayer = currentPlayer;
+ this.notifyMatchResumed(drawPhase);
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+
+
+ /**
+ * Initialize everything about the match.
+ *
+ * @param playersUsernamesAndPawns Map from player's username to pawn color
+ * @param playersHands Map from player's username to hand
+ * @param visibleObjectives Common objectives
+ * @param visiblePlayableCards Common cards that can be drawn
+ * @param decksTopReign The two decks' top cards
+ */
+ private void setupMatch(Map<String, Color> playersUsernamesAndPawns, Map<String, List<PlayableCard>> playersHands,
+ Pair<Objective, Objective> visibleObjectives, Map<DrawSource, PlayableCard> visiblePlayableCards,
+ Pair<Symbol, Symbol> decksTopReign) {
+ synchronized (sync) {
+ this.players = new ArrayList<>();
+ this.clientBoards = new HashMap<>();
+ Color curr;
+ playersUsernamesAndPawns.forEach((player, pawn) -> this.players.add(player));
+
+ for (String username : playersUsernamesAndPawns.keySet()) {
+ curr = playersUsernamesAndPawns.get(username);
+ switch (curr) {
+ case Color.RED:
+ this.players.set(0, username);
+ break;
+ case Color.BLUE:
+ this.players.set(1, username);
+ break;
+ case Color.GREEN:
+ this.players.set(2, username);
+ break;
+ case Color.YELLOW:
+ this.players.set(3, username);
+ break;
+ default:
+ break;
+ }
+ }
+
+ this.currentPlayer = null;
+
+ playersHands.forEach((username, hand) -> {
+ this.clientBoards.put(username, new ClientBoard(playersUsernamesAndPawns.get(username), hand));
+ });
+
+ this.visiblePlayableCards = visiblePlayableCards;
+ this.visibleObjectives = visibleObjectives;
+ this.decksTopReign = decksTopReign;
+ matchStarted = true;
+ sync.notifyAll();
+ }
+ }
+ /**
+ * Method that shows the user that the match has started.
+ */
+ protected abstract void notifyMatchStarted();
+
+ /**
+ * Method that shows the user that the match has resumed.
+ */
+ protected abstract void notifyMatchResumed(boolean drawPhase);
+
+ /**
+ * Receive the list of matches currently available
+ * @param availableMatches the list of available matches
+ */
+ public void receiveAvailableMatches(List<AvailableMatch> availableMatches) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ this.availableMatches = availableMatches;
+ }
+
+ /**
+ * Give the user its initial card.
+ *
+ * @param initialCard the player's initial card
+ */
+ public void giveInitialCard(InitialCard initialCard) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ this.clientBoards.get(this.username).setInitial(initialCard);
+ }
+
+
+ /**
+ * Gives the player two secret objectives to choose from.
+ *
+ * @param secretObjectives the two objectives to choose from
+ */
+ public void giveSecretObjectives(Pair<Objective, Objective> secretObjectives) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+
+ /**
+ * Notifies other players that someone drew the initial card.
+ *
+ * @param someoneUsername Player who drew the initial
+ * @param card The card he drew
+ */
+ public void someoneDrewInitialCard(String someoneUsername, InitialCard card) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ new Thread( () -> {
+ synchronized (sync) {
+ while (!matchStarted) {
+ try {
+ sync.wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ this.clientBoards.get(someoneUsername).setInitial(card);
+ }
+ }).start();
+ }
+
+
+ /**
+ * Effectively place the initial card on the player's board, on the right side. Note that the card
+ * must have already been set.
+ *
+ * @param availableResources Currently available resources for the player
+ * @param someoneUsername Player who chose the initial card's side
+ * @param side Chosen side
+ */
+ public void someoneSetInitialSide(String someoneUsername, Side side, Map<Symbol, Integer> availableResources) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ this.clientBoards.get(someoneUsername).placeInitial(side, availableResources);
+ this.nextPlayer();
+ }
+
+
+ /**
+ * Notifies other players that someone is choosing the secret objective. They should not know from
+ * which objective he is choosing, so they are not passed.
+ *
+ * @param someoneUsername Player who is choosing
+ */
+ public void someoneDrewSecretObjective(String someoneUsername) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ }
+
+ /**
+ * Changes the current player and updates last request's status to {@link RequestStatus#SUCCESSFUL}.
+ *
+ * @param someoneUsername The player who chose the objective
+ */
+ public void someoneChoseSecretObjective(String someoneUsername) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ this.nextPlayer();
+ }
+
+
+ /**
+ * Actually places a card on the player's board (so the Player tried to place a card and it was a
+ * valid move).
+ *
+ * @param someoneUsername The player who made the move
+ * @param coords where he placed the card
+ * @param card the placed card
+ * @param side the side the card was placed on
+ * @param points the total points of the player after he placed the card
+ * @param availableResources the available resources of the player after he placed the card
+ */
+ public void someonePlayedCard(String someoneUsername, Pair<Integer, Integer> coords, PlayableCard card, Side side, int points,
+ Map<Symbol, Integer> availableResources) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ if (points >= 20 && !this.lastTurn) {
+ this.lastTurn = true;
+ this.notifyLastTurn();
+ }
+ this.clientBoards.get(someoneUsername).placeCard(coords, card, side, points, availableResources);
+ }
+
+
+ /**
+ * Handles the replacement of the last card drawn, and changes turn.
+ *
+ * @param someoneUsername Player who drew the card
+ * @param source From where he drew the card
+ * @param card The card he drew
+ * @param replacementCard The replacement card, which will be null if the {@link DrawSource} is a
+ * deck
+ * @param deckTopReigns Current deck top reigns
+ */
+ public void someoneDrewCard(String someoneUsername, DrawSource source, PlayableCard card, PlayableCard replacementCard,
+ Pair<Symbol, Symbol> deckTopReigns) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ if (!source.equals(DrawSource.GOLDS_DECK) && !source.equals(DrawSource.RESOURCES_DECK)) {
+ visiblePlayableCards.put(source, replacementCard);
+ }
+ this.decksTopReign = deckTopReigns;
+
+ if (decksTopReign.first() == null && decksTopReign.second() == null && !this.lastTurn) {
+ this.lastTurn = true;
+ this.notifyLastTurn();
+ }
+ this.clientBoards.get(someoneUsername).drawCard(card);
+
+ this.nextPlayer();
+ }
+
+ /**
+ * Notifies the player that this is the last turn he can play.
+ */
+ public void notifyLastTurn() {
+ this.lastTurn = true;
+ }
+
+ /**
+ * Notifies the player that someone joined the lobby.
+ *
+ * @param joinedPlayers List of the players currently in the match
+ * @param someoneUsername Player who joined
+ */
+ public void someoneJoined(String someoneUsername, List<String> joinedPlayers) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ }
+
+ /**
+ * Notifies the player that someone quit the lobby.
+ *
+ * @param someoneUsername Player who quit
+ */
+ public abstract void someoneQuit(String someoneUsername);
+
+
+ /**
+ * Shows the player the match's leaderboard after the game ended.
+ *
+ * @param ranking Ranking of players
+ */
+ public abstract void matchFinished(List<LeaderboardEntry> ranking);
+
+ /**
+ * Notifies that someone sent a broadcast text.
+ *
+ * @param someoneUsername Player who sent the text
+ * @param text Text he sent
+ */
+ public void someoneSentBroadcastText(String someoneUsername, String text) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ }
+
+ /**
+ * Notifies the player that someone sent him a private text.
+ *
+ * @param someoneUsername Player who sent the private text
+ * @param text Text he sent
+ */
+ public void someoneSentPrivateText(String someoneUsername, String text) {
+ if (this.username.equals(someoneUsername)) {
+ this.setLastRequestStatus(RequestStatus.SUCCESSFUL);
+ }
+ }
+
+ /**
+ * Notify the client that the connection with the server
+ * has been lost.
+ */
+ public abstract void notifyConnectionLost();
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.html
new file mode 100644
index 00000000..d73fea39
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.html
@@ -0,0 +1 @@
+LastRequest
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.java.html
new file mode 100644
index 00000000..007d5449
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/LastRequest.java.html
@@ -0,0 +1,27 @@
+LastRequest.java
package it.polimi.ingsw.client.frontend;
+
+import it.polimi.ingsw.utils.RequestStatus;
+
+/**
+ * Last request status. Used for synchronized methods
+ */
+public class LastRequest {
+ private RequestStatus status;
+
+ /**
+ * Sets the status.
+ *
+ * @param status The new status
+ */
+ public void setStatus(RequestStatus status) {
+ this.status = status;
+ }
+
+ /**
+ * @return The last request's status.
+ */
+ public RequestStatus getStatus() {
+ return this.status;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.html
new file mode 100644
index 00000000..4889575d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.html
@@ -0,0 +1 @@
+MatchStatus
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.java.html
new file mode 100644
index 00000000..04549ef7
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/MatchStatus.java.html
@@ -0,0 +1,17 @@
+MatchStatus.java
package it.polimi.ingsw.client.frontend;
+
+import it.polimi.ingsw.gamemodel.MatchState;
+
+/**
+ * Represents the current match macro-state from the client point of view.
+ * This is not supposed to substitute {@link MatchState}, but rather make it easier for clients
+ * to keep track of the current state of match, which is to say whether it's unused (LOBBY),
+ * in WaitState (WAIT_STATE) or being played (MATCH_STATE).
+ */
+public enum MatchStatus {
+ LOBBY,
+ WAIT_STATE,
+ MATCH_STATE,
+ FINAL_STATE
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.html
new file mode 100644
index 00000000..0affa663
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.html
@@ -0,0 +1 @@
+ShownCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.java.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.java.html
new file mode 100644
index 00000000..c6912c23
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/ShownCard.java.html
@@ -0,0 +1,16 @@
+ShownCard.java
package it.polimi.ingsw.client.frontend;
+
+import it.polimi.ingsw.gamemodel.Card;
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * ShownCard
+ *
+ * @param card The card to be shown
+ * @param side The side to be shown
+ * @param coords The coordinates to be shown on the card (if needed)
+ */
+public record ShownCard(Card card, Side side, Pair<Integer, Integer> coords) {
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/index.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/index.html
new file mode 100644
index 00000000..bd8524d0
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.frontend/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.frontend/index.source.html
new file mode 100644
index 00000000..30751e33
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.frontend/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.frontend
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.html
new file mode 100644
index 00000000..9afb535d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.html
@@ -0,0 +1 @@
+NetworkHandler
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.java.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.java.html
new file mode 100644
index 00000000..1441f7fc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandler.java.html
@@ -0,0 +1,377 @@
+NetworkHandler.java
package it.polimi.ingsw.client.network;
+
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.controllers.PlayerController;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.server.Server;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class used by a generic client to receive from and transmit to a remote {@link Server} instance and a
+ * remote {@link PlayerController} instance.
+ * It represents an abstract layer, being implemented by: {@link NetworkHandlerRMI} and {@link NetworkHandlerTCP}.
+ */
+public abstract class NetworkHandler implements RemoteViewInterface {
+ protected final GraphicalView graphicalView;
+ protected String username;
+ protected final String ipAddress;
+ protected final int port;
+ protected boolean connected = false;
+
+ /**
+ * Initialize the instance all its internal attributes.
+ *
+ * @param graphicalView The GraphicalView to be subscribed to this NetworkHandler instance
+ * @param ipAddress The server IP address
+ * @param port The server port
+ */
+ public NetworkHandler(GraphicalView graphicalView, String ipAddress, int port) {
+ this.graphicalView = graphicalView;
+ this.ipAddress = ipAddress;
+ this.port = port;
+ }
+
+ /**
+ * Periodically check the connection status
+ */
+ public void startConnectionCheck() {
+ // Create a thread pool of 1 thread
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+
+ Runnable pingServer = () -> {
+ if (!connected) {
+ // If the connection lost is already acknowledged, shutdown the executor
+ executor.shutdown();
+ } else {
+ if (!ping()) {
+ // If there is a connection error, notify the client and shutdown the executor
+ disconnect();
+ graphicalView.notifyConnectionLost();
+ executor.shutdown();
+ }
+ }
+ };
+
+ // Check every two second for connectivity
+ executor.scheduleAtFixedRate(pingServer, 0, 2, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Sets the player's username.
+ *
+ * @param username The player's username
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Asks the server to send a list of {@link AvailableMatch}
+ */
+ public abstract void getAvailableMatches();
+
+ /**
+ * Checks for connectivity.
+ *
+ * @return The status of the connection, true if active, false otherwise
+ */
+ public abstract boolean ping();
+
+ /**
+ * Asks to create a match.
+ *
+ * @param matchName The match name
+ * @param maxPlayers The match maximum number of players
+ */
+ public abstract void createMatch(String matchName, Integer maxPlayers);
+
+ /**
+ * Asks to join a match.
+ *
+ * @param matchName the match's name
+ */
+ public abstract void joinMatch(String matchName);
+
+ /**
+ * Draws an initial card for the player.
+ */
+ public abstract void drawInitialCard();
+
+ /**
+ * Communicates the chosen initial card side.
+ *
+ * @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
+ */
+ public abstract void chooseInitialCardSide(Side side);
+
+ /**
+ * Draws two secret objectives.
+ */
+ public abstract void drawSecretObjectives();
+
+ /**
+ * Communicates the chosen secret objective.
+ *
+ * @param objective The chosen objective
+ */
+ public abstract void chooseSecretObjective(Objective objective);
+
+ /**
+ * Plays a card.
+ *
+ * @param coords The coordinates on which to place the card
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ */
+ public abstract void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side);
+
+ /**
+ * Draws a card.
+ *
+ * @param source The drawing source to draw the card from
+ */
+ public abstract void drawCard(DrawSource source);
+
+ /**
+ * Getter for the connection status.
+ *
+ * @return True if connected, false otherwise
+ */
+ public boolean isConnected() {
+ return connected;
+ }
+
+ /**
+ * Sends a message to all the match players
+ *
+ * @param text The content of the message
+ */
+ public abstract void sendBroadcastText(String text);
+
+ /**
+ * Sends a private message to a match player
+ *
+ * @param recipient The recipient username
+ * @param text The content of the message
+ */
+ public abstract void sendPrivateText(String recipient, String text);
+
+ /**
+ * Disconnects from the server.
+ */
+ public abstract void disconnect();
+
+ /**
+ * Receives the currently available matches.
+ *
+ * @param availableMatches The available matches
+ */
+ public void receiveAvailableMatches(List<AvailableMatch> availableMatches) {
+ graphicalView.receiveAvailableMatches(availableMatches);
+ }
+
+ /**
+ * Notifies that the match has just started.
+ * Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ * state.
+ *
+ * @param playersUsernamesAndPawns Map that matches each pawn color to the corresponding player's username
+ * @param playersHands Map that matches each player's username to the corresponding List of cards in the hand
+ * @param visibleObjectives Pair of objectives visible to all players
+ * @param visiblePlayableCards Map having as values the visible common cards (the keys are just indexes).
+ * @param decksTopReigns Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ * the first one is the gold deck one, the second one the resource deck one
+ */
+ @Override
+ public void matchStarted(Map<String, Color> playersUsernamesAndPawns, Map<String, List<PlayableCard>> playersHands,
+ Pair<Objective, Objective> visibleObjectives, Map<DrawSource, PlayableCard> visiblePlayableCards,
+ Pair<Symbol, Symbol> decksTopReigns) {
+ graphicalView.matchStarted(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReigns);
+ }
+
+ /**
+ * Notifies that the match has resumed.
+ * Furthermore, gives to the receiving object all the information (parameters) needed to show to the current match
+ * state.
+ *
+ * @param playersUsernamesAndPawns Map that matches each pawn color to the corresponding player's username
+ * @param playersHands Map that matches each player's username to the corresponding List of cards in the hand
+ * @param visibleObjectives Pair of objectives visible to all players
+ * @param visiblePlayableCards Map having as values the visible common cards (the keys are just indexes).
+ * @param decksTopReigns Pair of reign symbols representing the two visible reigns symbols on top of the two decks;
+ * the first one is the gold deck one, the second one the resource deck one
+ * @param secretObjective Secret objective of the current player
+ * @param availableResources Available resources of all the players
+ * @param placedCards Placed cards of all the players
+ * @param playerPoints Points of all the players
+ * @param currentPlayer The current player
+ * @param drawPhase If the match is resumed in draw phase
+ */
+ @Override
+ public void matchResumed(Map<String, Color> playersUsernamesAndPawns, Map<String, List<PlayableCard>> playersHands,
+ Pair<Objective, Objective> visibleObjectives, Map<DrawSource, PlayableCard> visiblePlayableCards,
+ Pair<Symbol, Symbol> decksTopReigns, Objective secretObjective, Map<String, Map<Symbol, Integer>> availableResources,
+ Map<String, Map<Pair<Integer, Integer>, PlacedCard>> placedCards, Map<String, Integer> playerPoints,
+ String currentPlayer, boolean drawPhase) {
+ graphicalView.resumeMatch(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReigns, secretObjective, availableResources, placedCards, playerPoints, currentPlayer, drawPhase);
+ }
+
+ /**
+ * Gives to the receiving graphical view (the client) its initial card.
+ *
+ * @param initialCard The initial card to be given
+ */
+ @Override
+ public void giveInitialCard(InitialCard initialCard) {
+ graphicalView.giveInitialCard(initialCard);
+ }
+
+ /**
+ * Gives to the remote object a pair of secret objectives to show them in the view and to choose one from them.
+ *
+ * @param secretObjectives Pair of secret objectives to give
+ */
+ @Override
+ public void giveSecretObjectives(Pair<Objective, Objective> secretObjectives) {
+ graphicalView.giveSecretObjectives(secretObjectives);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has drawn its initial card.
+ *
+ * @param someoneUsername The username of the player who has drawn the card
+ * @param card The card drawn
+ */
+ @Override
+ public void someoneDrewInitialCard(String someoneUsername, InitialCard card) {
+ graphicalView.someoneDrewInitialCard(someoneUsername, card);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has decided (then set) its initial card side.
+ *
+ * @param someoneUsername The username of the player who has set the initial card side
+ * @param side The chosen side
+ * @param availableResources The current available resources of the player having someoneUsername as username
+ */
+ @Override
+ public void someoneSetInitialSide(String someoneUsername, Side side, Map<Symbol, Integer> availableResources) {
+ graphicalView.someoneSetInitialSide(someoneUsername, side, availableResources);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has drawn a pair of secret objectives.
+ * Mind that the objectives are not passed as arguments, since they are secret to all players but the one receiving
+ * them. The one meant to receive them receives this message too but obtain the objectives through the
+ * giveSecretObjective() method.
+ *
+ * @param someoneUsername The username of the player who has drawn the card
+ */
+ @Override
+ public void someoneDrewSecretObjective(String someoneUsername) {
+ graphicalView.someoneDrewSecretObjective(someoneUsername);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has chosen theirs secret objective.
+ *
+ * @param someoneUsername The username of the player who has chosen theirs secret objective
+ */
+ @Override
+ public void someoneChoseSecretObjective(String someoneUsername) {
+ graphicalView.someoneChoseSecretObjective(someoneUsername);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has played a card.
+ *
+ * @param someoneUsername The username of the player who has played a card
+ * @param coords The coordinates where the card has been placed as a Pair of int
+ * @param card The card that has been played
+ * @param side The side on which the card has been played
+ * @param points The points of the player who played a card
+ * @param availableResources The current available resources of the player who played a card
+ */
+ @Override
+ public void someonePlayedCard(String someoneUsername, Pair<Integer, Integer> coords, PlayableCard card, Side side, int points,
+ Map<Symbol, Integer> availableResources) {
+ graphicalView.someonePlayedCard(someoneUsername, coords, card, side, points, availableResources);
+ }
+
+ /**
+ * Notifies that someone (it may or may not be the receiving View instance) has drawn a card.
+ *
+ * @param someoneUsername The username of the player who has played a card
+ * @param source The DrawSource from which the card has been drawn
+ * @param card The card that has been drawn
+ * @param replacementCard The card that replaced the drawn one
+ * @param deckTopReigns The decks top reigns
+ */
+ @Override
+ public void someoneDrewCard(String someoneUsername, DrawSource source, PlayableCard card, PlayableCard replacementCard,
+ Pair<Symbol, Symbol> deckTopReigns) {
+ graphicalView.someoneDrewCard(someoneUsername, source, card, replacementCard, deckTopReigns);
+ }
+
+ /**
+ * Notifies that a player has joined the match.
+ *
+ * @param someoneUsername The username of the player who has joined
+ * @param joinedPlayers The players currently in the match
+ */
+ @Override
+ public void someoneJoined(String someoneUsername, List<String> joinedPlayers) {
+ graphicalView.someoneJoined(someoneUsername, joinedPlayers);
+ }
+
+ /**
+ * Notifies that a player has quit from the match.
+ *
+ * @param someoneUsername The username of the player who has quit
+ */
+ @Override
+ public void someoneQuit(String someoneUsername) {
+ graphicalView.someoneQuit(someoneUsername);
+ }
+
+ /**
+ * Notifies that the match has just finished.
+ *
+ * @param ranking The match final ranking
+ */
+ @Override
+ public void matchFinished(List<LeaderboardEntry> ranking) {
+ graphicalView.matchFinished(ranking);
+ }
+
+ /**
+ * Notifies that a new message in the global chat is sent
+ *
+ * @param someoneUsername Username of the user that sent the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentBroadcastText(String someoneUsername, String text) {
+ graphicalView.someoneSentBroadcastText(someoneUsername, text);
+ }
+
+ /**
+ * Notifies that a new private message is sent in private chat to the current user
+ *
+ * @param someoneUsername Username of the user that sent the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentPrivateText(String someoneUsername, String text) {
+ graphicalView.someoneSentPrivateText(someoneUsername, text);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.html
new file mode 100644
index 00000000..a1483108
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.html
@@ -0,0 +1 @@
+NetworkHandlerRMI
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.java.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.java.html
new file mode 100644
index 00000000..d4705f2f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerRMI.java.html
@@ -0,0 +1,238 @@
+NetworkHandlerRMI.java
package it.polimi.ingsw.client.network;
+
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.List;
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.controllers.PlayerController;
+import it.polimi.ingsw.controllers.PlayerControllerRMI;
+import it.polimi.ingsw.controllers.PlayerControllerRMIInterface;
+import it.polimi.ingsw.gamemodel.DrawSource;
+import it.polimi.ingsw.gamemodel.Objective;
+import it.polimi.ingsw.gamemodel.PlayableCard;
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.server.Server;
+import it.polimi.ingsw.server.ServerRMIInterface;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * Class used by a generic client to receive from and transmit to a remote {@link Server} instance and a
+ * remote {@link PlayerControllerRMI} instance using the RMI protocol.
+ */
+public class NetworkHandlerRMI extends NetworkHandler {
+ private final ServerRMIInterface server;
+ private PlayerControllerRMIInterface controller;
+ private boolean exported = false;
+
+ /**
+ * Initialize the instance all its internal attributes.
+ *
+ * @param graphicalView The GraphicalView to be subscribed to this NetworkHandler instance
+ * @param ipAddress The server IP address
+ * @param port The server port
+ * @throws RemoteException If the remote server is considered not reachable any more and cannot return as usual
+ */
+ public NetworkHandlerRMI(GraphicalView graphicalView, String ipAddress, int port) throws RemoteException {
+ super(graphicalView, ipAddress, port);
+
+ // Try to get a remote Server instance from the network
+ Registry registry = LocateRegistry.getRegistry(ipAddress, port);
+ try {
+ this.server = (ServerRMIInterface) registry.lookup("CodexNaturalisRMIServer");
+ connected = true;
+ this.startConnectionCheck();
+ } catch (NotBoundException e) {
+ // If the registry exists but the lookup string isn't found, exit the application since it's
+ // a programmatic error (it regards the code, not the app life cycle)
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Asks the server to send a list of {@link AvailableMatch}.
+ */
+ @Override
+ public void getAvailableMatches() {
+ try {
+ List<AvailableMatch> matches = server.getJoinableMatches();
+ this.receiveAvailableMatches(matches);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Asks to join a match.
+ *
+ * @param matchName the match's name
+ */
+ @Override
+ public void joinMatch(String matchName) {
+ try {
+ controller = server.joinMatch(matchName, this.username);
+
+ // Export the object only if it was not previously exported
+ if (!exported) {
+ UnicastRemoteObject.exportObject(this, 0);
+ exported = true;
+ }
+ controller.registerView(this);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Asks to create a match.
+ *
+ * @param matchName The match name
+ * @param maxPlayers The match maximum number of players
+ */
+ @Override
+ public void createMatch(String matchName, Integer maxPlayers) {
+ try {
+ server.createMatch(matchName, maxPlayers);
+ this.joinMatch(matchName);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Draws an initial card for the player.
+ */
+ @Override
+ public void drawInitialCard() {
+ try {
+ controller.drawInitialCard();
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Communicates the chosen initial card side.
+ *
+ * @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
+ */
+ @Override
+ public void chooseInitialCardSide(Side side) {
+ try {
+ controller.chooseInitialCardSide(side);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Draws two secret objectives.
+ */
+ @Override
+ public void drawSecretObjectives() {
+ try {
+ controller.drawSecretObjectives();
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Communicates the chosen secret objective.
+ *
+ * @param objective The chosen objective
+ */
+ @Override
+ public void chooseSecretObjective(Objective objective) {
+ try {
+ controller.chooseSecretObjective(objective);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Plays a card.
+ *
+ * @param coords The coordinates on which to place the card
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ */
+ @Override
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ try {
+ controller.playCard(coords, card, side);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Draws a card.
+ *
+ * @param source The drawing source to draw the card from
+ */
+ @Override
+ public void drawCard(DrawSource source) {
+ try {
+ controller.drawCard(source);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Sends a message to all the match players
+ *
+ * @param text The content of the message
+ */
+ @Override
+ public void sendBroadcastText(String text) {
+ try {
+ controller.sendBroadcastText(text);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Sends a private message to a match player
+ *
+ * @param recipient The recipient username
+ * @param text The content of the message
+ */
+ @Override
+ public void sendPrivateText(String recipient, String text) {
+ try {
+ controller.sendPrivateText(recipient, text);
+ } catch (Exception e) {
+ this.graphicalView.notifyError(e);
+ }
+ }
+
+ /**
+ * Disconnects from the server.
+ */
+ @Override
+ public void disconnect() {
+ connected = false;
+ }
+
+ /**
+ * Checks for connectivity.
+ *
+ * @return The status of the connection, true if active, false otherwise
+ */
+ @Override
+ public boolean ping() {
+ try {
+ return server.ping();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.html
new file mode 100644
index 00000000..d291e82d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.html
@@ -0,0 +1 @@
+NetworkHandlerTCP
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.java.html b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.java.html
new file mode 100644
index 00000000..fcad082e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/NetworkHandlerTCP.java.html
@@ -0,0 +1,220 @@
+NetworkHandlerTCP.java
package it.polimi.ingsw.client.network;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.rmi.RemoteException;
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.controllers.PlayerControllerTCP;
+import it.polimi.ingsw.gamemodel.DrawSource;
+import it.polimi.ingsw.gamemodel.Objective;
+import it.polimi.ingsw.gamemodel.PlayableCard;
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.network.messages.Message;
+import it.polimi.ingsw.network.messages.actions.*;
+import it.polimi.ingsw.network.tcp.ClientReceiver;
+import it.polimi.ingsw.network.tcp.IOHandler;
+import it.polimi.ingsw.server.Server;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * Class used by a generic client to receive from and transmit to a remote {@link Server} instance
+ * and a remote {@link PlayerControllerTCP} instance using the TCP protocol.
+ */
+public class NetworkHandlerTCP extends NetworkHandler {
+ private final IOHandler io;
+ private final Socket socket;
+
+ /**
+ * Initialize the instance all its internal attributes.
+ *
+ * @param graphicalView The GraphicalView to be subscribed to this NetworkHandler instance
+ * @param ipAddress The server IP address
+ * @param port The server port
+ * @throws RemoteException If the remote server is considered not reachable any more and cannot
+ * return as usual
+ */
+ public NetworkHandlerTCP(GraphicalView graphicalView, String ipAddress, Integer port)
+ throws IOException {
+ super(graphicalView, ipAddress, port);
+ this.socket = new Socket(ipAddress, port);
+ this.io = new IOHandler(socket);
+ new Thread(new ClientReceiver(this, socket)).start();
+ connected = true;
+ super.startConnectionCheck();
+ }
+
+ /**
+ * Notifies the view about a remote error.
+ *
+ * @param exception The exception thrown remotely
+ */
+ public void notifyError(Exception exception) {
+ this.graphicalView.notifyError(exception);
+ }
+
+ /**
+ * Gets the player's username.
+ *
+ * @return The player's username
+ */
+ public String getUsername() {
+ return this.username;
+ }
+
+ /**
+ * Gets the I/O handler.
+ *
+ * @return The I/O handler
+ */
+ public IOHandler getIO() {
+ return this.io;
+ }
+
+ /**
+ * Asks the server to send a list of {@link AvailableMatch}.
+ */
+ @Override
+ public void getAvailableMatches() {
+ this.sendMessage(new GetAvailableMatchesMessage(this.username));
+ }
+
+ /**
+ * Asks to create a match.
+ *
+ * @param matchName The match name
+ * @param maxPlayers The match maximum number of players
+ */
+ @Override
+ public void createMatch(String matchName, Integer maxPlayers) {
+ this.sendMessage(new CreateMatchMessage(this.username, matchName, maxPlayers));
+ }
+
+ /**
+ * Asks to join a match.
+ *
+ * @param matchName the match's name
+ */
+ @Override
+ public void joinMatch(String matchName) {
+ this.sendMessage(new JoinMatchMessage(this.username, matchName));
+ }
+
+ /**
+ * Draws an initial card for the player.
+ */
+ @Override
+ public void drawInitialCard() {
+ this.sendMessage(new DrawInitialCardMessage(this.username));
+ }
+
+ /**
+ * Communicates the chosen initial card side.
+ *
+ * @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
+ */
+ @Override
+ public void chooseInitialCardSide(Side side) {
+ this.sendMessage(new ChooseInitialCardSideMessage(this.username, side));
+ }
+
+ /**
+ * Draws two secret objectives.
+ */
+ @Override
+ public void drawSecretObjectives() {
+ this.sendMessage(new DrawSecretObjectivesMessage(this.username));
+ }
+
+ /**
+ * Communicates the chosen secret objective.
+ *
+ * @param objective The chosen objective
+ */
+ @Override
+ public void chooseSecretObjective(Objective objective) {
+ this.sendMessage(new ChooseSecretObjectiveMessage(this.username, objective.getID()));
+ }
+
+ /**
+ * Plays a card.
+ *
+ * @param coords The coordinates on which to place the card
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ */
+ @Override
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ this.sendMessage(new PlayCardMessage(this.username, coords, card.getId(), side));
+ }
+
+ /**
+ * Draws a card.
+ *
+ * @param source The drawing source to draw the card from
+ */
+ @Override
+ public void drawCard(DrawSource source) {
+ this.sendMessage(new DrawCardMessage(this.username, source));
+ }
+
+ /**
+ * Sends a message to all the match players
+ *
+ * @param text The content of the message
+ */
+ @Override
+ public void sendBroadcastText(String text) {
+ this.sendMessage(new SendBroadcastTextMessage(this.username, text));
+ }
+
+ /**
+ * Sends a private message to a match player
+ *
+ * @param recipient The recipient username
+ * @param text The content of the message
+ */
+ @Override
+ public void sendPrivateText(String recipient, String text) {
+ this.sendMessage(new SendPrivateTextMessage(this.username, recipient, text));
+ }
+
+ /**
+ * Disconnects from the server.
+ */
+ @Override
+ public void disconnect() {
+ connected = false;
+ }
+
+ /**
+ * Checks for connectivity.
+ *
+ * @return The status of the connection, true if active, false otherwise
+ */
+ @Override
+ public boolean ping() {
+ try {
+ io.writeMsg("ping");
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+
+ /**
+ * Utility to send a message to the socket's output stream. If there was an error, it means the
+ * connection crashed, and so tries to disconnect the player
+ *
+ * @param msg The message to send
+ */
+ private void sendMessage(Message msg) {
+ try {
+ this.io.writeMsg(msg);
+ } catch (IOException e) {
+ this.disconnect();
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/index.html b/deliveries/Test results/it.polimi.ingsw.client.network/index.html
new file mode 100644
index 00000000..abdb4a03
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.network
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.client.network/index.source.html b/deliveries/Test results/it.polimi.ingsw.client.network/index.source.html
new file mode 100644
index 00000000..1aa918ce
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.client.network/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.client.network
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.html
new file mode 100644
index 00000000..72458087
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.html
@@ -0,0 +1 @@
+PlayerController
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.java.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.java.html
new file mode 100644
index 00000000..675cb3ba
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerController.java.html
@@ -0,0 +1,101 @@
+PlayerController.java
package it.polimi.ingsw.controllers;
+
+import it.polimi.ingsw.exceptions.AlreadyUsedUsernameException;
+import it.polimi.ingsw.exceptions.ChosenMatchException;
+import it.polimi.ingsw.exceptions.WrongNameException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.gamemodel.Match;
+import it.polimi.ingsw.gamemodel.MatchObserver;
+import it.polimi.ingsw.gamemodel.Player;
+import it.polimi.ingsw.utils.GuiUtil;
+
+import java.util.Optional;
+
+/**
+ * Controller for a match player, the only agent needing a view and so a controller in this
+ * application. This class subclasses instances are given (in RMI case) / reachable (in TCP case) on
+ * the network and collected by a corresponding view (RMI view or TCP view); then this class commits
+ * its two subclasses {@link PlayerControllerRMI} and {@link PlayerControllerTCP} to implement all
+ * the methods needed by a generic view to play in a match. This class implements
+ * {@link MatchObserver} since its instances subscribe themselves to a Match, as mentioned in
+ * {@link #PlayerController(String, Match)}; this is needed to allow this class to behave as a
+ * bridge between a view and a match.
+ */
+public abstract sealed class PlayerController implements MatchObserver permits PlayerControllerRMI, PlayerControllerTCP {
+ protected Player player;
+ protected final Match match;
+
+ /**
+ * Instantiates the internal Player with the given username and sets the internal Match reference to
+ * the given one, furthermore add the new Player instance to the match and subscribe this class
+ * instance to the match observers.
+ *
+ * @param username The username of the new player of the Match
+ * @param match The match to which this PlayerClass must pertain
+ */
+ public PlayerController(String username, Match match) {
+ this.player = new Player(username, match);
+ this.match = match;
+ }
+
+ /**
+ * Gets the player linked to this PlayerController instance.
+ *
+ * @return The player linked to this instance
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ /**
+ * Tries to effectively join a match, adding himself to the list of observers and the corresponding
+ * player to the match, if the username is valid.
+ *
+ * @throws AlreadyUsedUsernameException If the username is already taken
+ * @throws WrongStateException If the match currently does not accept new players
+ * @throws ChosenMatchException If the chosen match is not valid
+ * @throws WrongNameException If the chosen username is not acceptable due to alphabetical restrictions
+ * @throws IllegalArgumentException If the player is already in the match or too many players would be in the match
+ */
+ public void sendJoined() throws IllegalArgumentException, AlreadyUsedUsernameException, WrongStateException, ChosenMatchException, WrongNameException {
+ if (!GuiUtil.isValidName(this.player.getUsername())) {
+ throw new WrongNameException("The match name must be alphanumeric with maximum 32 characters");
+ }
+ if (match == null) {
+ throw new ChosenMatchException("The specified match does not exist");
+ }
+
+ try {
+ synchronized (match) {
+ if (!match.isRejoinable()) {
+ match.subscribeObserver(this);
+ match.addPlayer(this.player);
+ } else {
+ // Rejoin a match
+ // Get the player with the same username and not already connected
+ Optional<Player> playerOptional = match.getPlayers().stream()
+ .filter((p) -> p.getUsername().equals(player.getUsername()))
+ .filter((p) -> !p.isConnected())
+ .findFirst();
+ if (playerOptional.isPresent()) {
+ player = playerOptional.get();
+ player.setConnected(true);
+ match.subscribeObserver(this);
+ this.matchResumed();
+ } else {
+ throw new WrongStateException("There is no disconnected player with this username");
+ }
+ }
+ }
+ } catch (AlreadyUsedUsernameException | IllegalArgumentException e) {
+ match.unsubscribeObserver(this);
+ throw e;
+ }
+ }
+
+ /**
+ * Notifies the view that match has resumed after a server crash.
+ */
+ public abstract void matchResumed();
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.html
new file mode 100644
index 00000000..bb61e1b0
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.html
@@ -0,0 +1 @@
+PlayerControllerRMI
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.java.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.java.html
new file mode 100644
index 00000000..8ae149aa
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerRMI.java.html
@@ -0,0 +1,542 @@
+PlayerControllerRMI.java
package it.polimi.ingsw.controllers;
+
+import it.polimi.ingsw.client.network.RemoteViewInterface;
+import it.polimi.ingsw.exceptions.*;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.server.Server;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Subclass of {@link PlayerController} that implements its abstract methods through RMI interactions.
+ * Each instance of this class is supposed to be sent through {@link Server#joinMatch(String, String)})
+ * to an RMI View, this latter will then send its View instance to the PlayerController object, calling
+ * {@link #registerView(RemoteViewInterface)} on it.
+ */
+public final class PlayerControllerRMI extends PlayerController implements PlayerControllerRMIInterface {
+ // The remote View instance
+ private RemoteViewInterface view;
+
+ /**
+ * Instantiates the internal Player with the given username and sets the internal Match reference to the given one,
+ * add the new Player instance to the match and subscribe this class instance to the match observers.
+ *
+ * @param username The username of the new player of the Match
+ * @param match The match to which this PlayerClass must pertain
+ * @throws AlreadyUsedUsernameException If the username is already taken by another player of the same match
+ * @throws WrongStateException If a new player cannot be added on the current state of the Match
+ */
+ public PlayerControllerRMI(String username, Match match) throws AlreadyUsedUsernameException, WrongStateException {
+ super(username, match);
+ }
+
+ /**
+ * Notifies the view that match has resumed after a server crash.
+ */
+ @Override
+ public void matchResumed() {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ // Get visible objectives, visible playable cards and visible decks top reigns
+ Pair<Objective, Objective> visibleObjectives = match.getVisibleObjectives();
+ Map<DrawSource, PlayableCard> visiblePlayableCards = match.getVisiblePlayableCards();
+ Pair<Symbol, Symbol> decksTopReigns = match.getDecksTopReigns();
+
+ // Create a map that matches each pawn colour to the corresponding player's username
+ Map<String, Color> playersUsernamesAndPawns = new HashMap<>();
+
+ // Create a map that matches each player's username to the corresponding list of cards in the hand
+ Map<String, List<PlayableCard>> playersHands = new HashMap<>();
+
+ // Create a map that matches each player's username to the corresponding available resources
+ Map<String, Map<Symbol, Integer>> availableResources = new HashMap<>();
+
+ // Create a map that matches each player's username to the corresponding points
+ Map<String, Integer> playerPoints = new HashMap<>();
+
+ // Create a map that matches each player's username to the corresponding board
+ Map<String, Map<Pair<Integer, Integer>, PlacedCard>> playerBoards = new HashMap<>();
+
+ // Fill the maps with proper values
+ for (Player p : match.getPlayers()) {
+ playersUsernamesAndPawns.put(p.getUsername(), p.getPawnColor());
+ playersHands.put(p.getUsername(), p.getBoard().getCurrentHand());
+ availableResources.put(p.getUsername(), p.getBoard().getAvailableResources());
+ playerPoints.put(p.getUsername(), p.getPoints());
+ playerBoards.put(p.getUsername(), p.getBoard().getPlacedCards());
+ }
+
+ try {
+ view.matchResumed(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReigns,
+ player.getSecretObjective(), availableResources, playerBoards, playerPoints, match.getCurrentPlayer().getUsername(), match.getCurrentState().getClass().equals(AfterMoveState.class));
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Sets the internal View attribute to the given argument; if it has already been called, it won't
+ * do anything, since it's call is allowed once per PlayerController object.
+ * It's used by a remote View having this class object to send itself over RMI to the PlayerControllerRMI
+ * instance.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param view The View to save in the PlayerController internal state
+ */
+ @Override
+ public void registerView(RemoteViewInterface view) throws RemoteException, ChosenMatchException, WrongStateException, AlreadyUsedUsernameException, WrongNameException {
+ if (this.view == null) {
+ this.view = view;
+
+ this.sendJoined();
+ }
+ }
+
+ /**
+ * Draws an initial card for the player. Since this is done through RMI, it just involves a call to
+ * {@link Player#drawInitialCard()}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @throws WrongStateException If the current match state doesn't allow drawing an initial card
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ */
+ @Override
+ public void drawInitialCard() throws WrongStateException, WrongTurnException, RemoteException {
+ player.drawInitialCard();
+ }
+
+ /**
+ * Communicates the chosen initial card side. Since this is done through RMI, it just involves a call to
+ * {@link Player#chooseInitialCardSide(Side)}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param side The side on which play the initial card drawn using {@link #drawInitialCard()}
+ * @throws WrongStateException If the current match state doesn't allow setting the initial card side
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ */
+ @Override
+ public void chooseInitialCardSide(Side side) throws WrongStateException, WrongTurnException, RemoteException {
+ player.chooseInitialCardSide(side);
+ }
+
+ /**
+ * Draws two secret objectives. Since this is done through RMI, it just involves a call to
+ * {@link Player#drawSecretObjectives()}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @throws WrongStateException If the current match state doesn't allow drawing secret objectives
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ */
+ @Override
+ public void drawSecretObjectives() throws WrongStateException, WrongTurnException, RemoteException {
+ player.drawSecretObjectives();
+ }
+
+ /**
+ * Communicates the chosen secret objective. Since this is done through RMI, it just involves a call to
+ * {@link Player#chooseSecretObjective(Objective)}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param objective The chosen objective
+ * @throws WrongStateException If the current match state doesn't allow choosing a secret objective
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongChoiceException If the chosen objective is not one of the two drawn ones using {@link #drawSecretObjectives()}
+ */
+ @Override
+ public void chooseSecretObjective(Objective objective) throws WrongStateException, WrongTurnException, WrongChoiceException, RemoteException {
+ player.chooseSecretObjective(objective);
+ }
+
+ /**
+ * Plays a card. Since this is done through RMI, it just involves a call to
+ * {@link Player#playCard(Pair, PlayableCard, Side)}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param coords The coordinates on which to place the card
+ * @param card The PlayableCard to play
+ * @param side The side on which to play the chosen card
+ * @throws WrongStateException If the current match state doesn't allow playing cards
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongChoiceException If the chosen card is not one of those in the player's current hand
+ */
+ @Override
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) throws WrongStateException, WrongTurnException, WrongChoiceException, RemoteException {
+ player.playCard(coords, card, side);
+ }
+
+ /**
+ * Draws a card. Since this is done through RMI, it just involves a call to
+ * {@link Player#drawCard(DrawSource)}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param source The drawing source to draw the card from
+ * @throws HandException If the player already has a full hand of cards (three cards)
+ * @throws WrongStateException If the current match state doesn't allow drawing cards
+ * @throws WrongTurnException If the current turn it's not the one of this player
+ * @throws WrongChoiceException If the chosen DrawSource doesn't have any card left (i.e. it's empty)
+ */
+ @Override
+ public void drawCard(DrawSource source) throws HandException, WrongStateException, WrongTurnException, WrongChoiceException, RemoteException {
+ player.drawCard(source);
+ }
+
+ /**
+ * Sends a broadcast in the chat. Since this is done through RMI, it just involves a call to
+ * {@link Player#sendBroadcastText(String)}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param text Text of the message
+ */
+ @Override
+ public void sendBroadcastText(String text) throws RemoteException {
+ player.sendBroadcastText(text);
+ }
+
+ /**
+ * Sends a private message in the chat. Since this is done through RMI, it just involves a call to
+ * {@link Player#sendPrivateText(Player, String)} )}.
+ * Note that this method is supposed to be called by a view.
+ *
+ * @param recipient username of the recipient
+ * @param text text of the message
+ */
+ @Override
+ public void sendPrivateText(String recipient, String text) throws RemoteException {
+ if (match.getPlayers().stream().anyMatch(p -> p.getUsername().equals(recipient))) {
+ Player p = match.getPlayers().stream()
+ .filter(pl -> pl.getUsername().equals(recipient))
+ .toList().getFirst();
+ player.sendPrivateText(p, text);
+ }
+ }
+
+ /**
+ * Notifies that the match has just started.
+ * Note that is supposed to be called by the match.
+ */
+ @Override
+ public void matchStarted() {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ // Get visible objectives, visible playable cards and visible decks top reigns
+ Pair<Objective, Objective> visibleObjectives = match.getVisibleObjectives();
+ Map<DrawSource, PlayableCard> visiblePlayableCards = match.getVisiblePlayableCards();
+ Pair<Symbol, Symbol> decksTopReigns = match.getDecksTopReigns();
+
+ // Create a map that matches each pawn colour to the corresponding player's username
+ Map<String, Color> playersUsernamesAndPawns = new HashMap<>();
+
+ // Create a map that matches each player's username to the corresponding list of cards in the hand
+ Map<String, List<PlayableCard>> playersHands = new HashMap<>();
+
+ // Fill the maps with proper values
+ for (Player p : match.getPlayers()) {
+ playersUsernamesAndPawns.put(p.getUsername(), p.getPawnColor());
+ playersHands.put(p.getUsername(), p.getBoard().getCurrentHand());
+ }
+
+ try {
+ view.matchStarted(playersUsernamesAndPawns, playersHands, visibleObjectives, visiblePlayableCards, decksTopReigns);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has joined the match.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ * If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ * the view about the current lobby information.
+ *
+ * @param someone The Player instance that has joined
+ */
+ @Override
+ public void someoneJoined(Player someone) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ List<String> playersUsernames = match.getPlayers().stream().map(Player::getUsername).toList();
+ view.someoneJoined(someone.getUsername(), playersUsernames);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has quit from the match.
+ * Note that Match calls this method on all MatchObservers instance subscribed to itself, then
+ * even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The Player instance that has quit
+ */
+ @Override
+ public void someoneQuit(Player someone) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ view.someoneQuit(someone.getUsername());
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has drawn its initial card.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ * If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ * the view that it received an initial card.
+ *
+ * @param someone The player instance that has drawn the card
+ * @param card The initial card that has been drawn
+ */
+ @Override
+ public void someoneDrewInitialCard(Player someone, InitialCard card) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ if (player.equals(someone)) {
+ view.giveInitialCard(card);
+ } else {
+ view.someoneDrewInitialCard(someone.getUsername(), card);
+ }
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has chosen its initial card side.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The player instance that has chosen the side
+ * @param side The chosen initial card side
+ */
+ @Override
+ public void someoneSetInitialSide(Player someone, Side side, Map<Symbol, Integer> availableResources) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ view.someoneSetInitialSide(someone.getUsername(), side, availableResources);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has drawn two secret objectives.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ * If and only if the PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ * the view about the proposed objectives, the other views will just receive a notification about the player's username.
+ *
+ * @param someone The player instance that has drawn the objectives
+ * @param objectives The two proposed objectives
+ */
+ @Override
+ public void someoneDrewSecretObjective(Player someone, Pair<Objective, Objective> objectives) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ if (player.equals(someone)) {
+ view.giveSecretObjectives(objectives);
+ } else {
+ view.someoneDrewSecretObjective(someone.getUsername());
+ }
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has chosen the secret objective.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ * The view will just receive `someone` username, no the objective.
+ *
+ * @param someone The player instance that has chosen the secret objective
+ * @param objective The chosen secret objective
+ */
+ @Override
+ public void someoneChoseSecretObjective(Player someone, Objective objective) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ view.someoneChoseSecretObjective(someone.getUsername());
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has played a card.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The Player instance that has played a card
+ * @param coords The coordinates on which the card has been placed
+ * @param card The PlayableCard that has been played
+ * @param side The side on which the card has been placed
+ */
+ @Override
+ public void someonePlayedCard(Player someone, Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ view.someonePlayedCard(someone.getUsername(), coords, card, side, someone.getPoints(), someone.getBoard().getAvailableResources());
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone has drawn a card.
+ * The replacement card is the one that has taken the place of the drawn one, it's needed since observers have to
+ * know the reign of the new card on top of the decks.
+ * Note that this method is supposed to be called by a match, moreover the match calls this method on all the
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The Player instance that has drawn a card
+ * @param source The drawing source from which the card has been drawn
+ * @param card The card that has been drawn
+ * @param replacementCard The card that has replaced the drawn card, null if the draw source is a deck
+ */
+ @Override
+ public void someoneDrewCard(Player someone, DrawSource source, PlayableCard card, PlayableCard replacementCard) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ PlayableCard rep = null;
+ if (!source.equals(DrawSource.GOLDS_DECK) && !source.equals(DrawSource.RESOURCES_DECK)) {
+ rep = replacementCard;
+ }
+ view.someoneDrewCard(someone.getUsername(), source, card, rep, match.getDecksTopReigns());
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone sent a message in the public chat.
+ *
+ * @param someone The player that send the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentBroadcastText(Player someone, String text) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ view.someoneSentBroadcastText(someone.getUsername(), text);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Notifies that someone sent a private message to another user.
+ * If the recipient is the current player, then the view is notified,
+ * otherwise the message is ignored.
+ *
+ * @param someone The player that sent the message
+ * @param recipient The recipient of the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentPrivateText(Player someone, Player recipient, String text) {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ if (recipient.equals(this.player) || someone.equals(this.player)) {
+ try {
+ view.someoneSentPrivateText(someone.getUsername(), text);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a {@link LeaderboardEntry} instance from the given parameters.
+ *
+ * @param player The player of the {@link LeaderboardEntry}
+ * @param winner True if the player is the winner
+ * @return The new {@link LeaderboardEntry} instance
+ */
+ private LeaderboardEntry createLeaderboardEntry(Player player, Boolean winner) {
+ return new LeaderboardEntry(player.getUsername(), player.getPoints(), winner);
+ }
+
+ /**
+ * Notifies that the match has just finished.
+ */
+ @Override
+ public void matchFinished() {
+ if (view == null) {
+ onUnregisteredView();
+ } else {
+ try {
+ List<LeaderboardEntry> ranking = match.getPlayersFinalRanking().stream()
+ .map(p -> createLeaderboardEntry(p.first(), p.second())).toList();
+ view.matchFinished(ranking);
+ } catch (RemoteException e) {
+ onConnectionError();
+ }
+ }
+ }
+
+ /**
+ * Getter for the view associated to this instance.
+ *
+ * @return The {@link RemoteViewInterface} of this instance
+ */
+ public RemoteViewInterface getView() {
+ return view;
+ }
+
+ /**
+ * Removes the player linked to this PlayerControllerRMI instance when there's a connection error.
+ */
+ private void onConnectionError() {
+ match.unsubscribeObserver(this);
+ match.removePlayer(player);
+ }
+
+ /**
+ * Prints an error in stderr when this instance is being used without a view attached to it.
+ */
+ private void onUnregisteredView() {
+ System.err.println("The PlayerControllerRMI of player " + player.getUsername() + " hasn't got a corresponding view");
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.html
new file mode 100644
index 00000000..f784b8af
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.html
@@ -0,0 +1 @@
+PlayerControllerTCP
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.java.html b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.java.html
new file mode 100644
index 00000000..0c5c32f9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/PlayerControllerTCP.java.html
@@ -0,0 +1,427 @@
+PlayerControllerTCP.java
package it.polimi.ingsw.controllers;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import it.polimi.ingsw.exceptions.HandException;
+import it.polimi.ingsw.exceptions.WrongChoiceException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.exceptions.WrongTurnException;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.network.messages.Message;
+import it.polimi.ingsw.network.messages.errors.ErrorMessage;
+import it.polimi.ingsw.network.messages.responses.*;
+import it.polimi.ingsw.network.tcp.IOHandler;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.PlacedCardRecord;
+
+/**
+ * Subclass of {@link PlayerController} that implements its abstract methods through TCP
+ * interactions.
+ */
+public final class PlayerControllerTCP extends PlayerController {
+ private final IOHandler io;
+
+ /**
+ * Instantiates the internal Player with the given username and sets the internal Match
+ * reference to the given one, add the new Player instance to the match and subscribe this class
+ * instance to the match observers.
+ *
+ * @param username The username of the new player of the Match
+ * @param match The match to which this PlayerClass must pertain
+ * @param io The I/O handler to be attached to this instance
+ */
+ public PlayerControllerTCP(String username, Match match, IOHandler io) {
+ super(username, match);
+ this.io = io;
+ }
+
+ /**
+ * Utility method to send a message object over the network.
+ *
+ * @param msg The message object to be sent
+ */
+ private void sendMessage(Message msg) {
+ try {
+ this.io.writeMsg(msg);
+ } catch (Exception e) {
+ this.connectionError();
+ }
+ }
+
+ /**
+ * Utility method called when there's a connection error, it removes the player from the match.
+ */
+ private void connectionError() {
+ match.removePlayer(player);
+ match.unsubscribeObserver(this);
+ }
+
+ /**
+ * Utility method to create an {@link ErrorMessage} object from an exception.
+ *
+ * @param exception The exception
+ * @return The nex ErrorMessage instance
+ */
+ private ErrorMessage createErrorMessage(Exception exception) {
+ return new ErrorMessage(exception.getMessage(), exception.getClass().getName());
+ }
+
+ /**
+ * Notifies that the match has just started. Note that is supposed to be called by the match.
+ */
+ @Override
+ public void matchStarted() {
+ this.sendMessage(new MatchStartedMessage(match.getVisibleObjectives(),
+ match.getVisiblePlayableCards(), match.getDecksTopReigns(), match.getPlayers()));
+ }
+
+ /**
+ * Notifies that someone has joined the match. Note that this method is supposed to be called by
+ * a match, moreover the match calls this method on all the MatchObservers instance subscribed
+ * to itself, then even the MatchObserver causing this event gets notified. If and only if the
+ * PlayerController receiving this method call is the one linked to given `someone`, it notifies
+ * the view about the current lobby information.
+ *
+ * @param someone The Player instance that has joined
+ */
+ @Override
+ public void someoneJoined(Player someone) {
+ this.sendMessage(new SomeoneJoinedMessage(someone.getUsername(), match.getPlayers(),
+ match.getMaxPlayers()));
+ }
+
+ /**
+ * Notifies that someone has quit from the match. Note that Match calls this method on all
+ * MatchObservers instance subscribed to itself, then even the MatchObserver causing this event
+ * gets notified.
+ *
+ * @param someone The Player instance that has quit
+ */
+ @Override
+ public void someoneQuit(Player someone) {
+ this.sendMessage(new SomeoneQuitMessage(someone.getUsername(), match.getPlayers().size(),
+ match.isFinished()));
+ }
+
+ /**
+ * Notifies that someone has drawn its initial card. Note that this method is supposed to be
+ * called by a match, moreover the match calls this method on all the MatchObservers instance
+ * subscribed to itself, then even the MatchObserver causing this event gets notified. If and
+ * only if the PlayerController receiving this method call is the one linked to given `someone`,
+ * it notifies the view that it received an initial card.
+ *
+ * @param someone The player instance that has drawn the card
+ * @param card The initial card that has been drawn
+ */
+ @Override
+ public void someoneDrewInitialCard(Player someone, InitialCard card) {
+ this.sendMessage(new SomeoneDrewInitialCardMessage(someone.getUsername(), card.getId()));
+ }
+
+ /**
+ * Notifies that someone has chosen its initial card side. Note that this method is supposed to
+ * be called by a match, moreover the match calls this method on all the MatchObservers instance
+ * subscribed to itself, then even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The player instance that has chosen the side
+ * @param side The chosen initial card side
+ */
+ @Override
+ public void someoneSetInitialSide(Player someone, Side side,
+ Map<Symbol, Integer> availableResources) {
+ this.sendMessage(
+ new SomeoneSetInitialSideMessage(someone.getUsername(), side, availableResources));
+ }
+
+ /**
+ * Notifies that someone has drawn two secret objectives. Note that this method is supposed to
+ * be called by a match, moreover the match calls this method on all the MatchObservers instance
+ * subscribed to itself, then even the MatchObserver causing this event gets notified. If and
+ * only if the PlayerController receiving this method call is the one linked to given `someone`,
+ * it notifies the view about the proposed objectives, the other views will just receive a
+ * notification about the player's username.
+ *
+ * @param someone The player instance that has drawn the objectives
+ * @param objectives The two proposed objectives
+ */
+ @Override
+ public void someoneDrewSecretObjective(Player someone, Pair<Objective, Objective> objectives) {
+ Pair<Integer, Integer> IDs =
+ new Pair<>(objectives.first().getID(), objectives.second().getID());
+ this.sendMessage(new SomeoneDrewSecretObjectivesMessage(someone.getUsername(), IDs));
+ }
+
+ /**
+ * Notifies that someone has chosen the secret objective. Note that this method is supposed to
+ * be called by a match, moreover the match calls this method on all the MatchObservers instance
+ * subscribed to itself, then even the MatchObserver causing this event gets notified. The view
+ * will just receive `someone` username, no the objective.
+ *
+ * @param someone The player instance that has chosen the secret objective
+ * @param objective The chosen secret objective
+ */
+ @Override
+ public void someoneChoseSecretObjective(Player someone, Objective objective) {
+ Integer objectiveID = null;
+ if (someone.equals(player))
+ objectiveID = objective.getID();
+ this.sendMessage(
+ new SomeoneChoseSecretObjectiveMessage(someone.getUsername(), objectiveID));
+ }
+
+ /**
+ * Notifies that someone has played a card. Note that this method is supposed to be called by a
+ * match, moreover the match calls this method on all the MatchObservers instance subscribed to
+ * itself, then even the MatchObserver causing this event gets notified.
+ *
+ * @param someone The Player instance that has played a card
+ * @param coords The coordinates on which the card has been placed
+ * @param card The PlayableCard that has been played
+ * @param side The side on which the card has been placed
+ */
+ @Override
+ public void someonePlayedCard(Player someone, Pair<Integer, Integer> coords, PlayableCard card,
+ Side side) {
+ this.sendMessage(new SomeonePlayedCardMessage(someone.getUsername(), coords, card.getId(),
+ side, someone.getPoints(), someone.getBoard().getAvailableResources()));
+ }
+
+ /**
+ * Notifies that someone has drawn a card. The replacement card is the one that has taken the
+ * place of the drawn one, it's needed since observers have to know the reign of the new card on
+ * top of the decks. Note that this method is supposed to be called by a match, moreover the
+ * match calls this method on all the MatchObservers instance subscribed to itself, then even
+ * the MatchObserver causing this event gets notified.
+ *
+ * @param someone The Player instance that has drawn a card
+ * @param source The drawing source from which the card has been drawn
+ * @param card The card that has been drawn
+ * @param replacementCard The card that has replaced the drawn card, null if the draw source is
+ * a deck
+ */
+ @Override
+ public void someoneDrewCard(Player someone, DrawSource source, PlayableCard card,
+ PlayableCard replacementCard) {
+ Integer repId = null;
+ if (replacementCard != null) {
+ repId = replacementCard.getId();
+ }
+ this.sendMessage(new SomeoneDrewCardMessage(someone.getUsername(), source, card.getId(),
+ repId, match.getDecksTopReigns()));
+ }
+
+ /**
+ * Notifies that someone sent a message in the public chat.
+ *
+ * @param someone The player that send the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentBroadcastText(Player someone, String text) {
+ Message msg = new SomeoneSentBroadcastTextMessage(someone.getUsername(), text);
+ this.sendMessage(msg);
+ }
+
+ /**
+ * Notifies that someone sent a private message to another user. If the recipient is the current
+ * player, then the view is notified, otherwise the message is ignored.
+ *
+ * @param someone The player that sent the message
+ * @param recipient The recipient of the message
+ * @param text Content of the message
+ */
+ @Override
+ public void someoneSentPrivateText(Player someone, Player recipient, String text) {
+ if (recipient.getUsername().equals(this.player.getUsername())
+ || someone.getUsername().equals(this.player.getUsername())) {
+ Message msg = new SomeoneSentPrivateTextMessage(someone.getUsername(),
+ recipient.getUsername(), text);
+ this.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Notifies that the match has just finished.
+ */
+ @Override
+ public void matchFinished() {
+ this.sendMessage(new MatchFinishedMessage(match.getPlayersFinalRanking()));
+ }
+
+
+ /**
+ * Tries to get the player initial card, unless there was a {@link WrongTurnException} or a
+ * {@link WrongStateException}, in which case a new {@link ErrorMessage} is sent with the
+ * exception content
+ */
+ public void drawInitialCard() {
+ try {
+ this.player.drawInitialCard();
+ } catch (WrongTurnException | WrongStateException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+
+ /**
+ * Tries to set the player's initial card's side, unless there was a {@link WrongTurnException}
+ * or a {@link WrongStateException}, in which case a new {@link ErrorMessage} is sent with the
+ * exception content
+ *
+ * @param side The chosen card
+ */
+ public void chooseInitialCardSide(Side side) {
+ try {
+ this.player.chooseInitialCardSide(side);
+ } catch (WrongTurnException | WrongStateException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+ /**
+ * Tries to get the player's secret objectives pair (from which he will have to choose one),
+ * unless there was a {@link WrongTurnException} or a {@link WrongStateException}, in which case
+ * a new {@link ErrorMessage} is sent with the exception content
+ */
+ public void drawSecretObjectives() {
+ try {
+ this.player.drawSecretObjectives();
+ } catch (WrongTurnException | WrongStateException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+ /**
+ * Tries to set the player's secret objective, unless there was a {@link WrongTurnException} or
+ * a {@link WrongStateException}, in which case a new {@link ErrorMessage} is sent with the
+ * exception content
+ *
+ * @param objective The chosen objective
+ */
+ public void chooseSecretObjective(Objective objective) {
+ try {
+ this.player.chooseSecretObjective(objective);
+ } catch (WrongChoiceException | WrongStateException | WrongTurnException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+ /**
+ * Tries to place a card on the player's board, unless there was a {@link WrongStateException},
+ * in which case a new {@link ErrorMessage} is sent with the exception content
+ *
+ * @param coords The chosen coordinates
+ * @param card The chosen card
+ * @param side The chosen side
+ */
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ try {
+ this.player.playCard(coords, card, side);
+ } catch (WrongChoiceException | WrongStateException | WrongTurnException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+ /**
+ * Tries to draw a card, unless there was a {@link WrongStateException}, in which case a new
+ * {@link ErrorMessage} is sent with the exception content
+ *
+ * @param source The chosen source
+ */
+ public void drawCard(DrawSource source) {
+ try {
+ this.player.drawCard(source);
+ } catch (HandException | WrongTurnException | WrongStateException
+ | WrongChoiceException e) {
+ this.sendMessage(this.createErrorMessage(e));
+ }
+ }
+
+ /**
+ * Sends a broadcast in the chat.
+ *
+ * @param text Text of the message
+ */
+ public void sendBroadcastText(String text) {
+ this.player.sendBroadcastText(text);
+ }
+
+ /**
+ * Sends a private message in the chat.
+ *
+ * @param recipientUsername username of the recipient
+ * @param text text of the message
+ */
+ public void sendPrivateText(String recipientUsername, String text) {
+ Player recipient = null;
+ for (Player player : this.match.getPlayers()) {
+ if (player.getUsername().equals(recipientUsername)) {
+ recipient = player;
+ break;
+ }
+ }
+
+ // if you want to send error if recipient does not exist, change here
+ if (recipient != null) {
+ this.player.sendPrivateText(recipient, text);
+ }
+ }
+
+ /**
+ * Notifies the view that match has resumed after a server crash.
+ */
+ @Override
+ public void matchResumed() {
+ Map<String, Color> playersUsernamesAndPawns = new HashMap<>();
+ Map<String, List<Integer>> playersHands = new HashMap<>();
+ Pair<Integer, Integer> visibleObjectives;
+ Map<DrawSource, Integer> visiblePlayableCards = new HashMap<>();
+ Pair<Symbol, Symbol> decksTopReigns;
+ Integer secretObjective;
+ Map<String, Map<Symbol, Integer>> availableResources = new HashMap<>();
+ Map<String, Map<Integer, PlacedCardRecord>> placedCards = new HashMap<>();
+ Map<String, Integer> playerPoints = new HashMap<>();
+ String currentPlayer;
+ boolean drawPhase;
+
+ this.match.getPlayers().forEach(player -> {
+ String username = player.getUsername();
+ Board board = player.getBoard();
+ playersUsernamesAndPawns.put(username, player.getPawnColor());
+ playersHands.put(username,
+ board.getCurrentHand().stream().map(Card::getId).collect(Collectors.toList()));
+ availableResources.put(username, board.getAvailableResources());
+
+ Map<Integer, PlacedCardRecord> placed = new HashMap<>();
+ board.getPlacedCards()
+ .forEach((coords, placedCard) -> placed.put(placedCard.getTurn(),
+ new PlacedCardRecord(placedCard.getCard().getId(), coords.first(),
+ coords.second(), placedCard.getPlayedSide())));
+
+ placedCards.put(username, placed);
+ playerPoints.put(username, player.getPoints());
+ });
+
+ Pair<Objective, Objective> visibleObjectivesValue = this.match.getVisibleObjectives();
+ // Get a Set of Entry, which contains key and value, and create a new Hashmap with key and
+ // value.ID
+ visibleObjectives = new Pair<>(visibleObjectivesValue.first().getID(),
+ visibleObjectivesValue.second().getID());
+ visiblePlayableCards = this.match.getVisiblePlayableCards().entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getId()));
+ decksTopReigns = this.match.getDecksTopReigns();
+ secretObjective = this.player.getSecretObjective().getID();
+ currentPlayer = this.match.getCurrentPlayer().getUsername();
+ drawPhase = this.match.getCurrentState().getClass().equals(AfterMoveState.class);
+
+
+ Message msg = new MatchResumedMessage(playersUsernamesAndPawns, playersHands,
+ visibleObjectives, visiblePlayableCards, decksTopReigns, secretObjective,
+ availableResources, placedCards, playerPoints, currentPlayer, drawPhase);
+
+ this.sendMessage(msg);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/index.html b/deliveries/Test results/it.polimi.ingsw.controllers/index.html
new file mode 100644
index 00000000..6eba3ad7
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.controllers
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.controllers/index.source.html b/deliveries/Test results/it.polimi.ingsw.controllers/index.source.html
new file mode 100644
index 00000000..d0ecf593
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.controllers/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.controllers
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.html
new file mode 100644
index 00000000..fdf8f4e2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.html
@@ -0,0 +1 @@
+AlreadyUsedUsernameException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.java.html
new file mode 100644
index 00000000..7e9a4663
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/AlreadyUsedUsernameException.java.html
@@ -0,0 +1,8 @@
+AlreadyUsedUsernameException.java
package it.polimi.ingsw.exceptions;
+
+public class AlreadyUsedUsernameException extends Exception {
+ public AlreadyUsedUsernameException(String message) {
+ super(message);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.html
new file mode 100644
index 00000000..c8295abb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.html
@@ -0,0 +1 @@
+CardException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.java.html
new file mode 100644
index 00000000..c23631ce
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/CardException.java.html
@@ -0,0 +1,9 @@
+CardException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.html
new file mode 100644
index 00000000..379ddedc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.html
@@ -0,0 +1 @@
+ChosenMatchException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.java.html
new file mode 100644
index 00000000..ed5a9b54
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/ChosenMatchException.java.html
@@ -0,0 +1,8 @@
+ChosenMatchException.java
package it.polimi.ingsw.exceptions;
+
+public class ChosenMatchException extends Exception {
+ public ChosenMatchException(String message) {
+ super(message);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.html
new file mode 100644
index 00000000..b14e1fc6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.html
@@ -0,0 +1 @@
+DeckException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.java.html
new file mode 100644
index 00000000..18f79436
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/DeckException.java.html
@@ -0,0 +1,9 @@
+DeckException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.html
new file mode 100644
index 00000000..3dac5f28
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.html
@@ -0,0 +1 @@
+HandException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.java.html
new file mode 100644
index 00000000..acc0ee30
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/HandException.java.html
@@ -0,0 +1,9 @@
+HandException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.html
new file mode 100644
index 00000000..81d0e798
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.html
@@ -0,0 +1 @@
+InvalidPlayerException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.java.html
new file mode 100644
index 00000000..23b1fde5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidPlayerException.java.html
@@ -0,0 +1,11 @@
+InvalidPlayerException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.html
new file mode 100644
index 00000000..d472bf80
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.html
@@ -0,0 +1 @@
+InvalidResourceException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.java.html
new file mode 100644
index 00000000..1d1e47a4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/InvalidResourceException.java.html
@@ -0,0 +1,9 @@
+InvalidResourceException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.html
new file mode 100644
index 00000000..57ace49f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.html
@@ -0,0 +1 @@
+WrongChoiceException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.java.html
new file mode 100644
index 00000000..3c425d1c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongChoiceException.java.html
@@ -0,0 +1,8 @@
+WrongChoiceException.java
package it.polimi.ingsw.exceptions;
+
+public class WrongChoiceException extends Exception {
+ public WrongChoiceException(String s) {
+ super(s);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.html
new file mode 100644
index 00000000..a1619a18
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.html
@@ -0,0 +1 @@
+WrongInputFormatException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.java.html
new file mode 100644
index 00000000..d94cecc6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongInputFormatException.java.html
@@ -0,0 +1,12 @@
+WrongInputFormatException.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.html
new file mode 100644
index 00000000..2529e451
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.html
@@ -0,0 +1 @@
+WrongNameException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.java.html
new file mode 100644
index 00000000..b292e562
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongNameException.java.html
@@ -0,0 +1,8 @@
+WrongNameException.java
package it.polimi.ingsw.exceptions;
+
+public class WrongNameException extends Exception{
+ public WrongNameException(String message) {
+ super(message);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.html
new file mode 100644
index 00000000..99b7e1dd
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.html
@@ -0,0 +1 @@
+WrongStateException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.java.html
new file mode 100644
index 00000000..f8c79a24
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongStateException.java.html
@@ -0,0 +1,8 @@
+WrongStateException.java
package it.polimi.ingsw.exceptions;
+
+public class WrongStateException extends Exception {
+ public WrongStateException(String message) {
+ super(message);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.html
new file mode 100644
index 00000000..ed2f725d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.html
@@ -0,0 +1 @@
+WrongTurnException
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.java.html b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.java.html
new file mode 100644
index 00000000..18580a7c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/WrongTurnException.java.html
@@ -0,0 +1,8 @@
+WrongTurnException.java
package it.polimi.ingsw.exceptions;
+
+public class WrongTurnException extends Exception {
+ public WrongTurnException(String message) {
+ super(message);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/index.html b/deliveries/Test results/it.polimi.ingsw.exceptions/index.html
new file mode 100644
index 00000000..7dcea2bb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.exceptions
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.exceptions/index.source.html b/deliveries/Test results/it.polimi.ingsw.exceptions/index.source.html
new file mode 100644
index 00000000..cefa6ff5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.exceptions/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.exceptions
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.html
new file mode 100644
index 00000000..7dca272d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.html
@@ -0,0 +1 @@
+AfterDrawState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.java.html
new file mode 100644
index 00000000..7573a352
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterDrawState.java.html
@@ -0,0 +1,37 @@
+AfterDrawState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match recognize if it's finished or it has to continue.
+ */
+public class AfterDrawState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public AfterDrawState(Match match) {
+ super(match);
+ }
+
+ /**
+ * If the match isn't finished, transitions to {@link NextTurnState}, otherwise to {@link FinalState}.
+ */
+ @Override
+ public void transition() {
+ MatchState nextState;
+
+ if (match.isFinished())
+ nextState = new FinalState(match);
+ else
+ nextState = new NextTurnState(match);
+
+ match.setState(nextState);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.html
new file mode 100644
index 00000000..9e95f63a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.html
@@ -0,0 +1 @@
+AfterMoveState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.java.html
new file mode 100644
index 00000000..ef1a36e5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/AfterMoveState.java.html
@@ -0,0 +1,41 @@
+AfterMoveState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match allows a player to draw a card.
+ */
+public class AfterMoveState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public AfterMoveState(Match match) {
+ super(match);
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void drawCard() {
+
+ }
+
+ /**
+ * Transitions to {@link AfterDrawState}.
+ */
+ @Override
+ public void transition() {
+ MatchState nextState = new AfterDrawState(match);
+ match.setState(nextState);
+
+ nextState.transition();
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.html
new file mode 100644
index 00000000..53037699
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.html
@@ -0,0 +1 @@
+Board
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.java.html
new file mode 100644
index 00000000..d38b8c92
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Board.java.html
@@ -0,0 +1,255 @@
+Board.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.CardException;
+import it.polimi.ingsw.exceptions.HandException;
+import it.polimi.ingsw.utils.Pair;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Board is the class that contains all the information relative to a {@link Player}'s status
+ */
+public class Board implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final List<PlayableCard> currentHand;
+ private final Map<Pair<Integer, Integer>, PlacedCard> placed;
+ private final Map<Symbol, Integer> availableResources;
+
+ private static final Map<Pair<Integer, Integer>, Corner> diagonalOffsets = Map.of(
+ new Pair<>(-1, +1), Corner.BOTTOM_RIGHT,
+ new Pair<>(+1, +1), Corner.BOTTOM_LEFT,
+ new Pair<>(-1, -1), Corner.TOP_RIGHT,
+ new Pair<>(+1, -1), Corner.TOP_LEFT
+ );
+
+ /**
+ * Class constructor. No inputs taken as the board starts empty
+ */
+ public Board() {
+ currentHand = new ArrayList<>();
+ placed = new HashMap<>();
+ availableResources = new HashMap<>();
+ for (Symbol s : Symbol.getBasicResources()) {
+ availableResources.put(s, 0);
+ }
+ }
+
+ /**
+ * Getter for the total resources of a player
+ *
+ * @return the resources of a player
+ */
+ public Map<Symbol, Integer> getAvailableResources() {
+ return this.availableResources;
+ }
+
+ /**
+ * Getter for the board's placed cards
+ *
+ * @return map containing all the placed cards indexed by their coordinates
+ */
+ public Map<Pair<Integer, Integer>, PlacedCard> getPlacedCards() {
+ return this.placed;
+ }
+
+ /**
+ * Getter for the hand of the player (which must be composed of three {@link PlayableCard}), which is visible
+ * to every player
+ *
+ * @return the player's hand
+ */
+ public List<PlayableCard> getCurrentHand() {
+ return this.currentHand;
+ }
+
+ /**
+ * Removes a card from the hand of the player
+ *
+ * @param card the card that must be removed from the player's hand
+ * @throws HandException if the player does not have exactly 3 cards in his hand
+ */
+ protected void removeHandCard(PlayableCard card) throws HandException {
+ if (currentHand.size() != 3) {
+ throw new HandException("Tried to remove a card from an empty hand!");
+ }
+ currentHand.remove(card);
+ }
+
+ /**
+ * Adds a card to the player's hand (which is visible to every player)
+ *
+ * @param card the card to put in the hand
+ * @throws HandException if the player already has 3 cards
+ */
+ protected void addHandCard(PlayableCard card) throws HandException {
+ if (currentHand.size() > 2) { // la mano ha 3 carte max
+ throw new HandException("Tried to draw a card with a full hand!");
+ }
+ currentHand.addLast(card);
+ }
+
+ /**
+ * Places the initial card in the (0, 0) coordinates, on the desired side
+ *
+ * @param card the initial card
+ * @param side the desired side
+ * @throws CardException if the (0, 0) position is already occupied
+ */
+ protected void setInitialCard(InitialCard card, Side side) throws CardException {
+ if (placed.get(new Pair<>(0, 0)) != null) {
+ throw new CardException("Tried to add initial card, but one already exists!");
+ }
+ placed.put(new Pair<>(0, 0), new PlacedCard(card, side, 0));
+
+ increaseResources(card, side);
+
+ for (Symbol s : card.getSide(side).getCenter()) {
+ if (Symbol.getBasicResources().contains(s)) {
+ availableResources.put(s, availableResources.get(s) + 1);
+ }
+ }
+ }
+
+ /**
+ * This method will add to the board the given card (assuming the positioning is valid), and update the player's resources
+ *
+ * @param coord the x and y coordinates in which the card must be placed
+ * @param card the card to be placed
+ * @param side the side of the card to be placed
+ * @param turn the turn of the game in which the card is played
+ * @return the points gained from playing card
+ * @throws CardException if the card type is not known (neither a {@link ResourceCard} nor a {@link GoldCard})
+ */
+ protected int placeCard(Pair<Integer, Integer> coord, PlayableCard card, Side side, int turn) throws CardException {
+ PlacedCard last = new PlacedCard(card, side, turn);
+ this.placed.put(coord, last);
+ int points = 0;
+
+ Symbol cornerSymbol;
+ Integer x = coord.first();
+ Integer y = coord.second();
+
+ for (Pair<Integer, Integer> diagOffset : diagonalOffsets.keySet()) {
+ try {
+ cornerSymbol = this.getSymbolIfPresent(new Pair<>(x + diagOffset.first(), y + diagOffset.second()), diagonalOffsets.get(diagOffset));
+ if (cornerSymbol != null) {
+ if (Symbol.getBasicResources().contains(cornerSymbol)) {
+ availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) - 1);
+ }
+ }
+ } catch (CardException e) {
+ System.err.println(e.getMessage());
+ }
+ }
+
+ increaseResources(card, side);
+
+ for (Symbol s : card.getSide(side).getCenter()) {
+ if (Symbol.getBasicResources().contains(s)) {
+ availableResources.put(s, availableResources.get(s) + 1);
+ }
+ }
+ if (side.equals(Side.FRONT)) {
+ switch (card) {
+ case GoldCard gold -> points = gold.calculatePoints(this, coord);
+ case ResourceCard resource -> points = resource.getPoints();
+ default -> throw new CardException("Unknown card type: " + card.getClass() + "!");
+ }
+ }
+
+ return points;
+ }
+
+ private Symbol getSymbolIfPresent(Pair<Integer, Integer> coord, Corner corner) throws CardException {
+ PlacedCard placedCard = placed.get(coord);
+ if (placedCard == null) {
+ return null;
+ }
+ return placedCard.getCard().getSide(placedCard.getPlayedSide()).getCorner(corner);
+ }
+
+ /**
+ * Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card),
+ * the given coordinates must be valid, and if the card has a requirement it must be met
+ *
+ * @param coord the coordinates in which the card should be played
+ * @param card the card to check on
+ * @param side the side of the card (needed for requirement check)
+ * @return the outcome for the placement, which is valid only if all conditions are met
+ * @throws CardException if the card is not in the player's hand
+ */
+ public PlacementOutcome verifyCardPlacement(Pair<Integer, Integer> coord, Card card, Side side) throws CardException {
+ if (coord.equals(new Pair<>(0, 0))) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+ if (!currentHand.contains(card)) {
+ throw new CardException("The card is not in the player's hand!");
+ }
+ if (placed.containsKey(coord)) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+ if (card instanceof GoldCard gold && side == Side.FRONT) {
+ if (gold.getRequirement().timesMet(this) == 0)
+ return PlacementOutcome.INVALID_ENOUGH_RESOURCES;
+ }
+
+
+ Integer[] offsets = {-1, +1};
+
+ Pair<Integer, Integer> cmp;
+
+ // cross-check: none exists
+ for (Integer offset : offsets) {
+ cmp = new Pair<>(coord.first() + offset, coord.second());
+ if (placed.containsKey(cmp)) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+
+ cmp = new Pair<>(coord.first(), coord.second() + offset);
+ if (placed.containsKey(cmp)) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+ }
+
+ boolean hasAdjacent = false;
+
+ Integer x = coord.first();
+ Integer y = coord.second();
+
+ for (Pair<Integer, Integer> diagOffset : Board.diagonalOffsets.keySet()) {
+ cmp = new Pair<>(x + diagOffset.first(), y + diagOffset.second());
+
+ if (placed.get(cmp) != null) {
+ hasAdjacent = true;
+ if (placed.get(cmp).getPlayedCardFace().getCorner(diagonalOffsets.get(diagOffset)) == Symbol.EMPTY_CORNER) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+ }
+ }
+
+ if (!hasAdjacent) {
+ return PlacementOutcome.INVALID_COORDS;
+ }
+
+ return PlacementOutcome.VALID;
+ }
+
+ private void increaseResources(Card card, Side side) throws CardException {
+ Symbol cornerSymbol;
+ for (Corner c : Corner.values()) {
+ cornerSymbol = card.getSide(side).getCorner(c);
+ if (Symbol.getBasicResources().contains(cornerSymbol)) {
+ availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) + 1);
+ }
+ }
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.html
new file mode 100644
index 00000000..051e3caa
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.html
@@ -0,0 +1 @@
+Card
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.java.html
new file mode 100644
index 00000000..ca33d821
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Card.java.html
@@ -0,0 +1,61 @@
+Card.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Highest abstraction of the card object, with common aspects for every card in
+ * the game (except objectives).
+ */
+public abstract class Card implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ protected CardFace front;
+ protected CardFace back;
+ protected Integer id;
+
+ /**
+ * Empty constructor used for deserialization.
+ */
+ public Card() {
+
+ }
+
+ /**
+ * Getter for the required side of the card
+ *
+ * @param side the required side
+ * @return the structure of the specified side
+ * @see CardFace
+ */
+ public CardFace getSide(Side side) {
+ return switch (side) {
+ case FRONT -> this.front;
+ case BACK -> this.back;
+ };
+ }
+
+
+ /**
+ * @return The card ID
+ */
+ public Integer getId() {
+ return this.id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Card card)) return false;
+
+ return this.id.equals(card.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.html
new file mode 100644
index 00000000..f41fc5da
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.html
@@ -0,0 +1 @@
+CardFace
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.java.html
new file mode 100644
index 00000000..79c8bfab
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/CardFace.java.html
@@ -0,0 +1,64 @@
+CardFace.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Set;
+import it.polimi.ingsw.exceptions.CardException;
+
+/**
+ * Topological definition of a card's side
+ */
+public class CardFace implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Symbol topLeft;
+ private Symbol topRight;
+ private Symbol bottomLeft;
+ private Symbol bottomRight;
+ private Set<Symbol> center;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param topLeft Top left corner
+ * @param topRight Top right corner
+ * @param bottomLeft Bottom left corner
+ * @param bottomRight Bottom right corner
+ * @param center Center of the card
+ */
+ public CardFace(Symbol topLeft, Symbol topRight, Symbol bottomLeft, Symbol bottomRight, Set<Symbol> center) {
+ this.topLeft = topLeft;
+ this.topRight = topRight;
+ this.bottomLeft = bottomLeft;
+ this.bottomRight = bottomRight;
+ this.center = center;
+ }
+
+ /**
+ * Used to get the symbol present in one of the four corners of a card
+ *
+ * @param corner which of the four corners we want
+ * @return the symbol the specified corner contains
+ * @throws CardException if the specified corner does not exist
+ */
+ public Symbol getCorner(Corner corner) throws CardException {
+ return switch (corner) {
+ case TOP_LEFT -> this.topLeft;
+ case TOP_RIGHT -> this.topRight;
+ case BOTTOM_LEFT -> this.bottomLeft;
+ case BOTTOM_RIGHT -> this.bottomRight;
+ };
+ }
+
+ /**
+ * Getter for the center of the card
+ *
+ * @return the set containing all symbols the center of the card contains
+ */
+ public Set<Symbol> getCenter() {
+ return center;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.html
new file mode 100644
index 00000000..67b3f135
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.html
@@ -0,0 +1 @@
+ChooseInitialSideState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.java.html
new file mode 100644
index 00000000..83a877e6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseInitialSideState.java.html
@@ -0,0 +1,44 @@
+ChooseInitialSideState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match is giving an initial card to a player and
+ * waiting him to choose its side.
+ */
+public class ChooseInitialSideState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public ChooseInitialSideState(Match match) {
+ super(match);
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void chooseInitialSide() {
+
+ }
+
+ /**
+ * Transitions to {@link NextTurnState}
+ */
+ @Override
+ public void transition() {
+ Player lastPlayer = match.getPlayers().getLast();
+
+ if (match.getCurrentPlayer().equals(lastPlayer))
+ match.doInitialTurnFinish();
+ MatchState nextState = new NextTurnState(match);
+ match.setState(nextState);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.html
new file mode 100644
index 00000000..816a2e7f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.html
@@ -0,0 +1 @@
+ChooseSecretObjectiveState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.java.html
new file mode 100644
index 00000000..61205e1c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ChooseSecretObjectiveState.java.html
@@ -0,0 +1,45 @@
+ChooseSecretObjectiveState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match is giving two secret objectives to a player and
+ * waiting him to choose one of them.
+ */
+public class ChooseSecretObjectiveState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public ChooseSecretObjectiveState(Match match) {
+ super(match);
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void chooseSecretObjective() {
+
+ }
+
+ /**
+ * Transitions to {@link NextTurnState}
+ */
+ @Override
+ public void transition() {
+ Player lastPlayer = match.getPlayers().getLast();
+
+ if (match.getCurrentPlayer().equals(lastPlayer))
+ match.doStart();
+
+ MatchState nextState = new NextTurnState(match);
+ match.setState(nextState);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.html
new file mode 100644
index 00000000..21861058
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.html
@@ -0,0 +1 @@
+Color
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.java.html
new file mode 100644
index 00000000..38ce2391
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Color.java.html
@@ -0,0 +1,12 @@
+Color.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.html
new file mode 100644
index 00000000..5b166f6c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.html
@@ -0,0 +1 @@
+Corner
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.java.html
new file mode 100644
index 00000000..b6f0f75b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Corner.java.html
@@ -0,0 +1,12 @@
+Corner.java
package it.polimi.ingsw.gamemodel;
+
+/**
+ * All the corners of a card, which can contain a {@link Symbol}
+ */
+public enum Corner {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.html
new file mode 100644
index 00000000..b3b10565
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.html
@@ -0,0 +1 @@
+DrawSource
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.java.html
new file mode 100644
index 00000000..ae351d60
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/DrawSource.java.html
@@ -0,0 +1,32 @@
+DrawSource.java
package it.polimi.ingsw.gamemodel;
+
+/**
+ * All the sources a player can draw from: the decks and the four visible cards.
+ */
+public enum DrawSource {
+ /**
+ * Gold cards deck
+ */
+ GOLDS_DECK,
+ /**
+ * Resource cards deck
+ */
+ RESOURCES_DECK,
+ /**
+ * First gold card (first among all)
+ */
+ FIRST_VISIBLE,
+ /**
+ * Second gold card (second among all)
+ */
+ SECOND_VISIBLE,
+ /**
+ * First resource card (third among all)
+ */
+ THIRD_VISIBLE,
+ /**
+ * Second resource card (fourth among all)
+ */
+ FOURTH_VISIBLE
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.html
new file mode 100644
index 00000000..114e1670
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.html
@@ -0,0 +1 @@
+FinalState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.java.html
new file mode 100644
index 00000000..c84f280c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/FinalState.java.html
@@ -0,0 +1,41 @@
+FinalState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match is when it's finished, so players are
+ * allowed to leave.
+ */
+public class FinalState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public FinalState(Match match) {
+ super(match);
+
+ match.decideWinner();
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void removePlayer() {
+ // No more transition
+ }
+
+ /**
+ * This call doesn't have any effect, apart from logging it to stderr.
+ */
+ @Override
+ public void transition() {
+ System.err.println("Transition tried in final state");
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.html
new file mode 100644
index 00000000..71c5c283
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.html
@@ -0,0 +1 @@
+GameDeck
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.java.html
new file mode 100644
index 00000000..7de332b2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/GameDeck.java.html
@@ -0,0 +1,96 @@
+GameDeck.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import it.polimi.ingsw.exceptions.DeckException;
+
+/**
+ * Generic used to create the decks for all the types of cards
+ *
+ * @param <T> the type of deck, which can be a {@link ResourceCard}, {@link GoldCard}, {@link InitialCard} or {@link Objective}
+ */
+public class GameDeck<T> implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private List<T> cardsList;
+
+ /**
+ * Class constructor, takes no argument as the decks start empty
+ */
+ public GameDeck() {
+ cardsList = new ArrayList<>();
+ }
+
+ /**
+ * Adds a card to the bottom of the deck
+ *
+ * @param card the card to add
+ */
+ public void add(T card) {
+ this.cardsList.addFirst(card);
+ }
+
+ /**
+ * Getter for the deck's size
+ *
+ * @return the deck's size
+ */
+ public int getSize() {
+ return this.cardsList.size();
+ }
+
+ /**
+ * Removes a card from the deck's top (throws exception if the deck is empty)
+ *
+ * @return the removed card
+ * @throws DeckException if the deck is empty
+ */
+ public T pop() throws DeckException {
+ if (this.isEmpty())
+ throw new DeckException("Tried to draw from an empty deck!");
+ return cardsList.removeLast();
+ }
+
+ /**
+ * Returns a card from the deck's top without removing it (returns null if empty)
+ *
+ * @return the card
+ */
+ public T peek() {
+ if (this.isEmpty())
+ return null;
+ return cardsList.getLast();
+ }
+
+ /**
+ * Removes a card from the deck's top (null if the deck is empty)
+ *
+ * @return the removed card (null if the deck is empty)
+ */
+ public T poll() {
+ if (this.isEmpty())
+ return null;
+ return cardsList.removeLast();
+ }
+
+ /**
+ * Shuffles the deck
+ */
+ public void shuffle() {
+ Collections.shuffle(this.cardsList);
+ }
+
+ /**
+ * Checks whether the deck is empty or not
+ *
+ * @return whether the deck is empty or not
+ */
+ public boolean isEmpty() {
+ return this.cardsList.isEmpty();
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.html
new file mode 100644
index 00000000..9bb1e869
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.html
@@ -0,0 +1 @@
+GoldCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.java.html
new file mode 100644
index 00000000..d423d3fe
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/GoldCard.java.html
@@ -0,0 +1,128 @@
+GoldCard.java
package it.polimi.ingsw.gamemodel;
+
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import it.polimi.ingsw.exceptions.InvalidResourceException;
+import it.polimi.ingsw.utils.Pair;
+
+
+/**
+ * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played
+ *
+ * @see CardFace
+ */
+public final class GoldCard extends PlayableCard {
+ private final Symbol multiplier;
+ private final QuantityRequirement req;
+
+ /**
+ * Class constructor. It needs to only take the front as an argument, since the back is handled by its superclass {@link PlayableCard}
+ *
+ * @param front the front side of the card
+ * @param reign the reign of the card
+ * @param multiplier the symbol whose number of resources multiplies the points parameter
+ * @param points the number every resource of the given type is worth
+ * @param req the requirement that must be met in order to be able to play the card
+ * @throws InvalidResourceException if the passed resource is not in {@link Symbol#getReigns()}
+ */
+ public GoldCard(CardFace front, Symbol reign, Symbol multiplier, int points, QuantityRequirement req) throws InvalidResourceException {
+ super(reign);
+ this.front = front;
+ this.points = points;
+ this.req = req; // integrity check already provided in the constructor of QuantityRequirement
+
+ // integrity check for allowed multipliers
+ EnumSet<Symbol> validMultiplier = Symbol.getValidMultiplier();
+ if (!validMultiplier.contains(multiplier)) {
+ throw new InvalidResourceException("Resource " + multiplier.toString() + " is not valid for a " + this.getClass());
+ }
+ this.multiplier = multiplier;
+ }
+
+
+ /**
+ * Getter for the GoldCard class
+ *
+ * @return the multiplier
+ */
+ public Symbol getMultiplier() {
+ return this.multiplier;
+ }
+
+ /**
+ * Getter for the GoldCard class
+ *
+ * @return the quantity requirement for the gold card to be played
+ */
+ public QuantityRequirement getRequirement() {
+ return this.req;
+ }
+
+ /**
+ * Getter for the GoldCard class
+ *
+ * @return points held by the card
+ */
+ public int getPoints() {
+ return this.points;
+ }
+
+ /**
+ * Will compute the total points this card gives based on the board it's played on.
+ * It MUST be called AFTER the placement of the gold card
+ *
+ * @param board the board on which we want to compute the points this card will give
+ * @param coord the coordinates of the card just placed (needed fot corner objectives)
+ * @return the points gained from playing the gold card
+ */
+ public int calculatePoints(Board board, Pair<Integer, Integer> coord) {
+ if (this.multiplier == Symbol.NO_MULT) {
+ return this.points;
+ }
+ Map<Symbol, Integer> availableResources = board.getAvailableResources();
+
+ int totalElements = 0;
+
+ // multiplier is basic resource (subset of symbols)
+ if (Symbol.getBasicResources().contains(this.multiplier)) {
+
+ for (Symbol s : availableResources.keySet()) {
+ if (s.equals(this.multiplier)) {
+ totalElements = availableResources.get(s);
+ }
+ }
+ } else if (this.multiplier.equals(Symbol.CORNER_OBJ)) { //multiplier is a corner_objective kind
+
+ // Pair<Integer, Integer> currentCoord = board.getCoordinatesPlacedCard();
+ Set<Pair<Integer, Integer>> edges = getEdges(coord);
+
+ Map<Pair<Integer, Integer>, PlacedCard> map = board.getPlacedCards();
+ for (Pair<Integer, Integer> p : edges) {
+
+ // check if the board has a value (card) associated to the key (coordinates)
+ if (map.get(p) != null) {
+ totalElements++;
+ }
+ }
+ }
+
+ return totalElements * this.points;
+ }
+
+ private static Set<Pair<Integer, Integer>> getEdges(Pair<Integer, Integer> currentCoord) {
+ Pair<Integer, Integer> tr = new Pair<>(currentCoord.first() + 1, currentCoord.second() + 1);
+ Pair<Integer, Integer> br = new Pair<>(currentCoord.first() + 1, currentCoord.second() - 1);
+ Pair<Integer, Integer> tl = new Pair<>(currentCoord.first() - 1, currentCoord.second() - 1);
+ Pair<Integer, Integer> bl = new Pair<>(currentCoord.first() - 1, currentCoord.second() + 1);
+
+ Set<Pair<Integer, Integer>> edges = new HashSet<>();
+ edges.add(tr);
+ edges.add(br);
+ edges.add(tl);
+ edges.add(bl);
+ return edges;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.html
new file mode 100644
index 00000000..a3ad4c7e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.html
@@ -0,0 +1 @@
+InitialCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.java.html
new file mode 100644
index 00000000..4ea1f1f8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/InitialCard.java.html
@@ -0,0 +1,30 @@
+InitialCard.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Every player has an initial card (which will automatically be placed in the
+ * center of the board)
+ */
+public class InitialCard extends Card implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private static Integer lastID = 0;
+
+ /**
+ * The initial card only gives corners and resources, never points, so we only
+ * need to know its topological description
+ *
+ * @param front the front side of the card
+ * @param back the back side of the card
+ */
+ public InitialCard(CardFace front, CardFace back) {
+ InitialCard.lastID++;
+ this.id = InitialCard.lastID;
+ this.front = front;
+ this.back = back;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.html
new file mode 100644
index 00000000..d4561a1f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.html
@@ -0,0 +1 @@
+Match
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.java.html
new file mode 100644
index 00000000..f26a0849
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Match.java.html
@@ -0,0 +1,818 @@
+Match.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import it.polimi.ingsw.exceptions.*;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * Represents the match played by {@link Player} instances, therefore implements a slice of game logic
+ * using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc.
+ * Other methods serve the purpose of being called by {@link MatchState} subclasses in order to notify the change
+ * of the current game state or trigger some changes in the match, such as setupBoards(...),
+ * doStart(...), etc.
+ * Few methods are called by the current player of the match, used to trigger a change in the match and so notify that
+ * an event occurred, such as nextPlayer(...).
+ */
+public class Match implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final List<Player> players;
+ private final int maxPlayers;
+ private Player currentPlayer;
+
+ private MatchState currentState;
+
+ // All cards decks
+ private final GameDeck<InitialCard> initialsDeck;
+ private final GameDeck<ResourceCard> resourcesDeck;
+ private final GameDeck<GoldCard> goldsDeck;
+ private final GameDeck<Objective> objectivesDeck;
+
+ // All the visible cards on the common table
+ private final Map<DrawSource, PlayableCard> visiblePlayableCards;
+ private Pair<Objective, Objective> visibleObjectives;
+
+ private Pair<Objective, Objective> currentProposedObjectives;
+ private InitialCard currentGivenInitialCard;
+
+ // Denotes if the match has been started/finished
+ private boolean started = false;
+ private boolean initialTurnFinished = false;
+ private boolean lastTurn = false;
+ private boolean finished = false;
+
+ // Current match turn as an integer (incremental)
+ private int turn;
+
+ // Players ranking of the match at the end of it.
+ // The List order represents the ranking order, the Boolean represent if the related player is a winner.
+ // This is needed since the match can end in a tie, in such case the first two/three players of the List will have a
+ // True flag.
+ private List<Pair<Player, Boolean>> playersFinalRanking;
+
+ // List of observers
+ private transient List<MatchObserver> observers;
+
+ /**
+ * Initializes main Match attributes and allocate the attribute players List, assuming no parameter is null.
+ *
+ * @param maxPlayers maximum number of players to be added to the match, chosen by the first player joining the match
+ * @param initialsDeck deck of initial cards
+ * @param resourcesDeck deck of resource cards
+ * @param goldsDeck deck of gold cards
+ * @param objectivesDeck deck of objectives
+ * @throws IllegalArgumentException if the decks provided do not have enough cards to start a game or maxPlayers are not 2,3,4
+ */
+ public Match(int maxPlayers, GameDeck<InitialCard> initialsDeck, GameDeck<ResourceCard> resourcesDeck, GameDeck<GoldCard> goldsDeck, GameDeck<Objective> objectivesDeck) throws IllegalArgumentException {
+ this.maxPlayers = maxPlayers;
+ this.initialsDeck = initialsDeck;
+ this.resourcesDeck = resourcesDeck;
+ this.goldsDeck = goldsDeck;
+ this.objectivesDeck = objectivesDeck;
+ this.currentState = new WaitState(this);
+
+ if (goldsDeck.getSize() < maxPlayers + 2)
+ throw new IllegalArgumentException("goldsDeck does not have enough cards");
+ else if (resourcesDeck.getSize() < maxPlayers * 2 + 2)
+ throw new IllegalArgumentException("resourcesDeck does not have enough cards");
+ else if (initialsDeck.getSize() < maxPlayers)
+ throw new IllegalArgumentException("initialDeck does not have enough cards");
+ else if (objectivesDeck.getSize() < 6)
+ throw new IllegalArgumentException("objectivesDeck does not have enough cards");
+ else if (maxPlayers < 2 || maxPlayers > 4)
+ throw new IllegalArgumentException("The players must be at least 2 or maximum 4");
+
+ this.players = new ArrayList<>();
+ this.visiblePlayableCards = new HashMap<>();
+ }
+
+ /**
+ * Adds a new player to the match, assuming it's not null.
+ * Note: Called by the Controller when a player joins the match.
+ *
+ * @param player player to be added to the match
+ * @throws WrongStateException if called while in a state that doesn't allow adding players
+ * @throws AlreadyUsedUsernameException if there is already a player in the match that has the same username
+ */
+ public void addPlayer(Player player) throws WrongStateException, AlreadyUsedUsernameException {
+ synchronized (this) {
+ List<String> playersUsernames = getPlayers().stream().map(Player::getUsername).toList();
+
+ if (playersUsernames.contains(player.getUsername()))
+ throw new AlreadyUsedUsernameException("The chosen username is already in use");
+
+ currentState.addPlayer();
+ players.add(player);
+ notifyObservers(observer -> observer.someoneJoined(player));
+ currentState.transition();
+ }
+ }
+
+ /**
+ * Removes a player from the match, assuming the player is in the match.
+ * Note: Called by the Controller when a player quits the match.
+ *
+ * @param player player to be removed from the match
+ */
+ public void removePlayer(Player player) {
+ synchronized (this) {
+ if (players.contains(player)) {
+ players.remove(player);
+ // If in a state different from the wait state, end the match
+ currentState.removePlayer();
+ notifyObservers(observer -> observer.someoneQuit(player));
+ }
+ }
+ }
+
+ /**
+ * Verifies if the match is full, thus no more players can join.
+ * Note: Used by the Controller
+ *
+ * @return true if the match is full, false otherwise
+ */
+ public boolean isFull() {
+ return !finished && players.size() == maxPlayers;
+ }
+
+ /**
+ * Modifies the current player according to the next turn: if it's the first turn, the current player is the first
+ * one in the players List, the turn order then follows the players List order, in a circular way.
+ * Ex. 1st -> 2nd -> 3rd ---> 1st -> 2nd etc.
+ * Note: Called by NextTurnState every time a new turn starts.
+ */
+ protected void nextPlayer() {
+ if (!players.isEmpty()) {
+ // If player has never been initialized OR the current player is the last one
+ if (currentPlayer == null || currentPlayer.equals(players.getLast())) {
+ // Set currentPlayer as the first one
+ currentPlayer = players.getFirst();
+
+ turn++;
+ } else {
+ // Get the index of the current player and choose the next one
+ int currentPlayerIndex = players.indexOf(currentPlayer);
+ currentPlayer = players.get(currentPlayerIndex + 1);
+ }
+ } else {
+ throw new RuntimeException("No players in the match, the next player cannot be set");
+ }
+ }
+
+ /**
+ * Verifies if the match is finished.
+ * Note: Called by the Controller and NextTurnState.
+ *
+ * @return true if the match is finished, false otherwise
+ */
+ public boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * Marks the initial turn as finished, assuming the initial turn hasn't finished yet.
+ * Called by ChooseInitialCardState once the initial turn is finished.
+ */
+ protected void doInitialTurnFinish() {
+ initialTurnFinished = true;
+ }
+
+ /**
+ * Verifies if the initial turn is finished.
+ * Note: Called by NextTurnState.
+ *
+ * @return true if the initial turn is finished, false otherwise
+ */
+ public boolean isInitialTurnFinished() {
+ return initialTurnFinished;
+ }
+
+ /**
+ * Marks the match as started, assuming the match hasn't started yet.
+ * Note: Called by ChooseSecretObjectiveState once the match is ready to start.
+ */
+ protected void doStart() {
+ started = true;
+ }
+
+ /**
+ * Verifies if the match is started.
+ * Note: Called by NextTurnState to check when to effectively start the match.
+ *
+ * @return true if the match is started, false otherwise
+ */
+ public boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Gets the player who's playing (or choosing the secret objective) at the moment.
+ * Note: Used by the Controller.
+ *
+ * @return the player playing at the moment, null if the match has never reached NextTurnState
+ */
+ public Player getCurrentPlayer() {
+ return currentPlayer;
+ }
+
+ /**
+ * Gets the match players.
+ *
+ * @return the match players in a List, dynamically defined as an ArrayList
+ */
+ public List<Player> getPlayers() {
+ return players;
+ }
+
+ /**
+ * Sets the current match state, assuming it's not null.
+ * Note: Called by each state to let the match enter to the next state.
+ *
+ * @param state the state in which the match has to be
+ */
+ protected void setState(MatchState state) {
+ this.currentState = state;
+ }
+
+ /**
+ * Draws a card from the initial cards deck
+ *
+ * @return the card drawn from the initial cards deck
+ * @throws WrongStateException if called while in a state that doesn't allow drawing an initial card
+ */
+ protected InitialCard drawInitialCard() throws WrongStateException {
+ currentState.drawInitialCard();
+
+ try {
+ currentGivenInitialCard = initialsDeck.pop();
+ } catch (DeckException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Notify observers and trigger state transition
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someoneDrewInitialCard(copy, currentGivenInitialCard));
+ currentState.transition();
+
+ return currentGivenInitialCard;
+ }
+
+ /**
+ * Extracts two cards from the deck of objectives and returns them.
+ * Note: Called by the Controller.
+ *
+ * @return two objective cards extracted from the objectives deck
+ */
+ protected Pair<Objective, Objective> proposeSecretObjectives() throws WrongStateException {
+ currentState.proposeSecretObjectives();
+ try {
+ Objective obj1 = objectivesDeck.pop();
+ Objective obj2 = objectivesDeck.pop();
+
+ currentProposedObjectives = new Pair<>(obj1, obj2);
+
+ // Notify observers and trigger state transition
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someoneDrewSecretObjective(copy, currentProposedObjectives));
+ currentState.transition();
+
+ return currentProposedObjectives;
+ } catch (DeckException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Checks that the given objective is one of the proposed ones to the current player
+ * and put the discarded objective back in the objectives deck.
+ * Note: Called by the current player
+ *
+ * @param objective the accepted objective by the player (NOT the discarded one)
+ */
+ protected void setSecretObjective(Objective objective) throws WrongChoiceException, WrongStateException {
+ currentState.chooseSecretObjective();
+
+ // Get proposed objectives
+ Objective firstProposedObjective = currentProposedObjectives.first();
+ Objective secondProposedObjective = currentProposedObjectives.second();
+
+ // Check if the chosen objective is one of the proposed ones and put it back in the deck
+ if (objective.equals(firstProposedObjective))
+ objectivesDeck.add(secondProposedObjective);
+ else if (objective.equals(secondProposedObjective))
+ objectivesDeck.add(firstProposedObjective);
+ else
+ // If the objective is not one of the proposed ones, throw an exception
+ throw new WrongChoiceException("The chosen objective is not one of the proposed ones");
+
+ // Notify observers and trigger state transition
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someoneChoseSecretObjective(copy, objective));
+ currentState.transition();
+ }
+
+ /**
+ * Shuffles the players turns order and gives them their pawn color.
+ * Note: Called by SetupState.
+ */
+ protected void setupPlayers() {
+ // Shuffle players List
+ Collections.shuffle(players);
+
+ // Set players' colors
+ for (int i = 0; i < maxPlayers; i++) {
+ players.get(i).setColor(Color.values()[i]);
+ }
+ }
+
+ /**
+ * Shuffles all thr cards decks and places the visible cards on the board
+ * Note: Called by SetupState.
+ */
+ protected void setupDecks() {
+ // Shuffle each deck
+ initialsDeck.shuffle();
+ resourcesDeck.shuffle();
+ goldsDeck.shuffle();
+ objectivesDeck.shuffle();
+
+ try {
+ // Pop two resources to be placed on the common table
+ ResourceCard resourceCard1 = resourcesDeck.pop();
+ ResourceCard resourceCard2 = resourcesDeck.pop();
+
+ // Pop two golds to be placed on the common table
+ GoldCard goldCard1 = goldsDeck.pop();
+ GoldCard goldCard2 = goldsDeck.pop();
+
+ // Pop two golds to be placed on the common table
+ Objective objective1 = objectivesDeck.pop();
+ Objective objective2 = objectivesDeck.pop();
+
+ // Put golds and resources in visiblePlayableCards
+ visiblePlayableCards.put(DrawSource.FIRST_VISIBLE, goldCard1);
+ visiblePlayableCards.put(DrawSource.SECOND_VISIBLE, goldCard2);
+ visiblePlayableCards.put(DrawSource.THIRD_VISIBLE, resourceCard1);
+ visiblePlayableCards.put(DrawSource.FOURTH_VISIBLE, resourceCard2);
+
+ visibleObjectives = new Pair<>(objective1, objective2);
+ } catch (DeckException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gives one gold card and two resource cards to each player (hand)
+ * and sets the initial card for each of them.
+ * Note: Called by WaitState.
+ */
+ protected void setupBoards() {
+ // Give starting cards to players
+ for (Player player : players) {
+ try {
+ // Pop a card from the resources deck and one from the golds deck
+ GoldCard goldCard = goldsDeck.pop();
+ ResourceCard resourceCard1 = resourcesDeck.pop();
+ ResourceCard resourceCard2 = resourcesDeck.pop();
+
+ // Add each card to the player's hand
+ player.getBoard().addHandCard(goldCard);
+ player.getBoard().addHandCard(resourceCard1);
+ player.getBoard().addHandCard(resourceCard2);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Makes the chosen move on the board of the current player (known because of the internal Match state);
+ * in particular, checks if the placement is valid, then places the card on the player's board and add points
+ * to the player.
+ * Note: Called by the current player.
+ *
+ * @param coords coordinates in which to place the card
+ * @param card card to place
+ * @param side side of the card to be placed
+ * @throws WrongStateException if called while in a state that doesn't allow making moves
+ * @throws WrongChoiceException if the move is not allowed (placement not allowed, or not enough resources, or card
+ * not in player's hand)
+ */
+ protected void makeMove(Pair<Integer, Integer> coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException {
+ currentState.makeMove();
+
+ Board currentPlayerBoard = currentPlayer.getBoard();
+
+ PlacementOutcome outcome;
+ try {
+ outcome = currentPlayerBoard.verifyCardPlacement(coords, card, side);
+ } catch (CardException e) {
+ throw new WrongChoiceException(e.getMessage());
+ }
+
+ // If placing the card in the current player's board is allowed by rules
+ switch (outcome) {
+ case VALID:
+ // Place the card in the current player's board
+ // and save the points possibly gained because of the move
+ int gainedPoints;
+ try {
+ gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn);
+ } catch (CardException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Remove the card from the player's hand
+ // since it has been placed on the board
+ try {
+ currentPlayerBoard.removeHandCard(card);
+ } catch (HandException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Update the current player's points
+ currentPlayer.addPoints(gainedPoints);
+
+ // If the current player reaches 20 points or more
+ // the last turn of the match starts
+ if (currentPlayer.getPoints() >= 20)
+ lastTurn = true;
+
+ // Notify observers and trigger state transition
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someonePlayedCard(copy, coords, card, side));
+ currentState.transition();
+
+ break;
+ case INVALID_COORDS:
+ throw new WrongChoiceException("Invalid coordinates!");
+ case INVALID_ENOUGH_RESOURCES:
+ throw new WrongChoiceException("Not enough resources!");
+ }
+ }
+
+ /**
+ * Draws a card from the passed source. If the caller wants to draw one of the four visible cards, a rule is applied:
+ * the first and second visible cards (FIRST/SECOND_VISIBLE) will be substituted by a gold card if possible, if not,
+ * by a resource card, if not again, they will be null (then not substituted); the first and second visible cards
+ * (THIRD/FOURTH_VISIBLE) will be substituted by a resource card if possible, if not, by a gold card, if not again,
+ * they will be null (then not substituted).
+ * Note: Called by the current player.
+ *
+ * @param source the source to draw a card from
+ * @return the card drawn
+ * @throws WrongStateException if called while in a state that doesn't allow making moves
+ * @throws WrongChoiceException if the source does not have cards
+ */
+ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException {
+ PlayableCard card;
+ PlayableCard replacementCard = null;
+
+ currentState.drawCard();
+
+ switch (source) {
+ case GOLDS_DECK -> {
+ try {
+ card = goldsDeck.pop();
+ replacementCard = goldsDeck.peek();
+ } catch (DeckException e) {
+ throw new WrongChoiceException("The gold cards deck is empty!");
+ }
+ }
+
+ case RESOURCES_DECK -> {
+ try {
+ card = resourcesDeck.pop();
+ replacementCard = resourcesDeck.peek();
+ } catch (DeckException e) {
+ throw new WrongChoiceException("The resource cards deck is empty!");
+ }
+ }
+
+ case FIRST_VISIBLE, SECOND_VISIBLE -> {
+ card = visiblePlayableCards.get(source);
+
+ // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played
+ // drawing the four visible cards, but they won't be substituted by others) throw an exception
+ if (card == null)
+ throw new WrongChoiceException("There is no visible card in the chosen position!");
+
+ // If the golds deck is NOT empty, substitute the first/second visible
+ // card with a new gold
+ if (!goldsDeck.isEmpty()) {
+ replacementCard = goldsDeck.poll();
+ visiblePlayableCards.put(source, replacementCard);
+ }
+ // If the golds deck is empty, substitute the first/second visible
+ // card with a resource
+ else {
+ replacementCard = resourcesDeck.poll();
+ visiblePlayableCards.put(source, replacementCard);
+ // If the resources deck is empty too, the GameDeck.poll() method returns null,
+ // then the corresponding visible card will be null
+ }
+ }
+
+ case THIRD_VISIBLE, FOURTH_VISIBLE -> {
+ card = visiblePlayableCards.get(source);
+
+ // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played
+ // drawing the four visible cards, but they won't be substituted by others) throw an exception
+ if (card == null)
+ throw new WrongChoiceException("There is no visible card in the chosen position!");
+
+ // If the resources deck is NOT empty, substitute the third/fourth visible
+ // card with a new resource
+ if (!resourcesDeck.isEmpty()) {
+ replacementCard = resourcesDeck.poll();
+ visiblePlayableCards.put(source, replacementCard);
+ }
+ // If the resources deck is empty, substitute the third/fourth visible
+ // card with a gold
+ else {
+ replacementCard = goldsDeck.poll();
+ visiblePlayableCards.put(source, replacementCard);
+ }
+ // If the golds deck is empty too, the GameDeck.poll() method returns null,
+ // then the corresponding visible card will be null
+ }
+
+ default -> throw new RuntimeException("Unexpected value of source");
+ }
+
+ if (goldsDeck.isEmpty() && resourcesDeck.isEmpty())
+ lastTurn = true;
+
+ // If the current player is the last one in the match turns rotation, i.e. the last one in the players List
+ // AND the current turn is the last one the match is now finished
+ if (currentPlayer.equals(players.getLast()) && lastTurn)
+ finished = true;
+
+ // Notify observers and trigger state transition
+ PlayableCard replacementCardFinal = replacementCard;
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someoneDrewCard(copy, source, card, replacementCardFinal));
+ currentState.transition();
+
+ return card;
+ }
+
+ /**
+ * Sets the current player's initial card side.
+ *
+ * @param side the side to put the initial card on
+ * @throws WrongStateException if called while in a state that doesn't allow choosing the initial card side
+ */
+ protected void setInitialSide(Side side, Map<Symbol, Integer> availableResources) throws WrongStateException {
+ currentState.chooseInitialSide();
+
+ try {
+ currentPlayer.getBoard().setInitialCard(currentGivenInitialCard, side);
+ } catch (CardException e) {
+ throw new RuntimeException(e);
+ }
+
+ currentGivenInitialCard = null;
+
+ // Notify observers and trigger state transition
+ Player copy = new Player(currentPlayer);
+ notifyObservers(observer -> observer.someoneSetInitialSide(copy, side, availableResources));
+ currentState.transition();
+ }
+
+ // Returns a Map that links each player to the number of DIFFERENT objectives achieved
+ private Map<Player, Integer> checkObjectivesAchievement() {
+ // Map that links each player to the number of DIFFERENT objectives achieved
+ Map<Player, Integer> playersAchievedObjectives = new HashMap<>();
+
+ Objective firstObjective = visibleObjectives.first();
+ Objective secondObjective = visibleObjectives.second();
+
+ for (Player p : players) {
+ Board board = p.getBoard();
+ Objective secretObjective = p.getSecretObjective();
+ int numAchievedObjectives = 0;
+
+ // Add to the player the points of the specific objective MULTIPLIED BY how many times they met the
+ // objective requirement
+ if (secretObjective != null)
+ p.addPoints(secretObjective.getPoints() * secretObjective.getReq().timesMet(board));
+ p.addPoints(firstObjective.getPoints() * firstObjective.getReq().timesMet(board));
+ p.addPoints(secondObjective.getPoints() * secondObjective.getReq().timesMet(board));
+
+ // Count the number of achieved objectives by the player
+ if (secretObjective != null && secretObjective.getReq().timesMet(board) >= 1)
+ numAchievedObjectives++;
+ if (firstObjective.getReq().timesMet(board) >= 1)
+ numAchievedObjectives++;
+ if (secondObjective.getReq().timesMet(board) >= 1)
+ numAchievedObjectives++;
+
+ playersAchievedObjectives.put(p, numAchievedObjectives);
+ }
+
+ return playersAchievedObjectives;
+ }
+
+ /**
+ * Calculates the winner (or winners)
+ */
+ protected void decideWinner() {
+ finished = true;
+ playersFinalRanking = new ArrayList<>();
+ Map<Player, Integer> achievedObjectives = checkObjectivesAchievement();
+
+ List<Player> sortedPlayers = players.stream()
+ .sorted(
+ // Create a comparator that firstly sorts based on player points
+ // and secondly, in case of same points, on the number of achieved objectives
+ // Please note: reversed() since the default sort is ascending (min first), but the expected
+ // results requires a descending sort (max points/objectives first)
+ Comparator.comparingInt(Player::getPoints)
+ .thenComparing(achievedObjectives::get)
+ .reversed()
+ )
+ .toList();
+
+ Player bestPlayer = sortedPlayers.getFirst();
+ int bestAchievedObjectives = achievedObjectives.get(bestPlayer);
+ int bestPoints = bestPlayer.getPoints();
+ boolean isWinner;
+
+ for (Player p : sortedPlayers) {
+ // If the current player has as many points and as many achieved objectives as the winner,
+ // then they're winner too
+ isWinner = p.getPoints() == bestPoints && achievedObjectives.get(p) == bestAchievedObjectives;
+
+ playersFinalRanking.add(new Pair<>(p, isWinner));
+ }
+
+ // Notify observers
+ notifyObservers(MatchObserver::matchFinished);
+ }
+
+ /**
+ * Getter for the final ranking of players. Return a valid result if and only if called when the match is in the
+ * FinalState, so it's finished.
+ *
+ * @return Players ranking of the match at the end of it; the List order represents the ranking order, the Boolean
+ * represent if the related player is a winner; this is needed since the match can end in a tie, in such case the
+ * first two/three players of the List will have a True flag
+ */
+ public List<Pair<Player, Boolean>> getPlayersFinalRanking() {
+ return playersFinalRanking;
+ }
+
+ /**
+ * Returns the visible objectives.
+ *
+ * @return a Pair containing the two visible objectives
+ */
+ public Pair<Objective, Objective> getVisibleObjectives() {
+ return visibleObjectives;
+ }
+
+ /**
+ * Getter for the four visible playable cards (i.e. resource cards and gold cards, not objectives) on the common
+ * table.
+ *
+ * @return a Map that links each visible playable card to a DrawSource (restricted to FIRST_VISIBLE, SECOND_VISIBLE,
+ * THIRD_VISIBLE, FOURTH_VISIBLE)
+ */
+ public Map<DrawSource, PlayableCard> getVisiblePlayableCards() {
+ return visiblePlayableCards;
+ }
+
+ /**
+ * Getter for the current match state.
+ *
+ * @return the current state of the match
+ */
+ public MatchState getCurrentState() {
+ return currentState;
+ }
+
+ /**
+ * Getter for the cards back on the top of the decks (i.e. those visible top cards).
+ * Both of them always contain just a reign.
+ *
+ * @return Pair of two reign Symbol (see {@link Symbol}.getReigns()), the first one regards the
+ * top card of gold cards deck, the second one regards the top card of resource cards deck
+ */
+ public Pair<Symbol, Symbol> getDecksTopReigns() {
+ PlayableCard goldCard = goldsDeck.peek();
+ PlayableCard resourceCard = resourcesDeck.peek();
+
+ Symbol goldReign;
+ Symbol resourceReign;
+
+ if (goldCard == null) {
+ goldReign = null;
+ } else {
+ goldReign = goldCard.getReign();
+ }
+
+ if (resourceCard == null) {
+ resourceReign = null;
+ } else {
+ resourceReign = resourceCard.getReign();
+ }
+
+ return new Pair<>(goldReign, resourceReign);
+ }
+
+ /**
+ * Getter for the maximum number of player for the match
+ *
+ * @return The maximum number of player
+ */
+ public int getMaxPlayers() {
+ return maxPlayers;
+ }
+
+ /**
+ * Adds the given MatchObserver to those observers notified on match events.
+ *
+ * @param observer The observer to be notified from now on when an event occurs
+ */
+ public void subscribeObserver(MatchObserver observer) {
+ if (observers == null) {
+ observers = new ArrayList<>();
+ }
+ observers.add(observer);
+ }
+
+ /**
+ * Removes the given MatchObserver to those observers notified on match events.
+ *
+ * @param observer The observer to be removed
+ */
+ public void unsubscribeObserver(MatchObserver observer) {
+ observers.remove(observer);
+ }
+
+ /**
+ * Notifies all match observers that the match has started.
+ * It's called by WaitState methods after the match setup, that's why it needs to be protected.
+ */
+ protected void notifyMatchStart() {
+ notifyObservers(MatchObserver::matchStarted);
+ }
+
+ /**
+ * Sends a broadcast message in the chat. Notifies all observers of the event.
+ * Called by Player
+ *
+ * @param sender player that sends the message
+ * @param text content of the message
+ */
+ protected void sendBroadcastText(Player sender, String text) {
+ notifyObservers(observer -> observer.someoneSentBroadcastText(sender, text));
+ }
+
+ /**
+ * Sends a private message to the recipient chat. Notifies all observers of the event.
+ * Called by Player
+ *
+ * @param sender player that sends the message
+ * @param recipient player that receives the message
+ * @param text content of the message
+ */
+ protected void sendPrivateText(Player sender, Player recipient, String text) {
+ notifyObservers(observer -> observer.someoneSentPrivateText(sender, recipient, text));
+ }
+
+ /**
+ * Notifies asynchronously all match observers, calling the passed MatchObserverCallable on each of them.
+ * To be more specific: creates a thread for each match observer, each thread is appointed to call the passed callable
+ * on a MatchObserver instance; then runs all of them; finally joins on them (waiting each of them to return and exit).
+ *
+ * @param observerCallable The "method" to be called on each observer of the match
+ */
+ private void notifyObservers(MatchObserverCallable observerCallable) {
+ if(observers == null || observers.isEmpty())
+ return;
+
+ ExecutorService executor = Executors.newFixedThreadPool(observers.size());
+
+ for (MatchObserver observer : observers)
+ executor.submit(() -> observerCallable.call(observer));
+
+ executor.shutdown();
+ }
+
+ /**
+ * If the match is rejoinable (not every player is connected)
+ * @return if the match is rejoinable
+ */
+ public synchronized boolean isRejoinable() {
+ return players.stream().anyMatch((p) -> !p.isConnected()) && isStarted();
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.html
new file mode 100644
index 00000000..f26ce6a4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.html
@@ -0,0 +1 @@
+MatchState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.java.html
new file mode 100644
index 00000000..b85cad93
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/MatchState.java.html
@@ -0,0 +1,131 @@
+MatchState.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.WrongStateException;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Represents an appendix of {@link Match}.
+ * Match fully delegates to this class, through composition, the role of keeping track of the current game state: it
+ * throws exceptions when a Match method is called while being in the wrong state and triggers some Match behavior
+ * (calling match.someMethod(...)) when a certain transition has occurred.
+ * Each method is supposed to be overridden by a subclass of MatchState if and only if it is allowed to be called from
+ * that specific subclass; if not overridden, the MatchState version of the method will be called and thus an exception
+ * will be thrown.
+ */
+public abstract class MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ protected Match match;
+
+ /**
+ * Empty constructor needed for deserialization.
+ */
+ public MatchState() {
+
+ }
+
+ protected MatchState(Match match) {
+ this.match = match;
+ }
+
+ /**
+ * Triggers the transition in Match from the current state to the next one, this means changing the current Match's
+ * MatchState instance to a new one, having the same static type (MatchState) but a different dynamic type (a subclass
+ * of MatchState).
+ */
+ public abstract void transition();
+
+ /**
+ * Checks dynamically if a player can be added in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void addPlayer() throws WrongStateException {
+ throw new WrongStateException("addPlayer not allowed from the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a player can be removed in the current state, otherwise it forces the Match to go to the
+ * FinalState.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState instance from which it's called (just WaitState overrides it).
+ */
+ public void removePlayer() {
+ // Exceptionally force the match to go to FinalState
+ // since a player has quit in a state that wasn't WaitState
+ match.setState(new FinalState(match));
+ }
+
+ /**
+ * Checks dynamically if an initial card can be drawn in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void drawInitialCard() throws WrongStateException {
+ throw new WrongStateException("drawInitialCard not allowed in the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a player can choose the initial card side in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void chooseInitialSide() throws WrongStateException {
+ throw new WrongStateException("chooseInitialSide not allowed in the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a player can make a move in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void makeMove() throws WrongStateException {
+ throw new WrongStateException("makeMove not allowed in the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a player can draw a playable card in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void drawCard() throws WrongStateException {
+ throw new WrongStateException("drawCard not allowed in the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a secret objective can be proposed to the current player in the current state, otherwise
+ * throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void proposeSecretObjectives() throws WrongStateException {
+ throw new WrongStateException("proposeSecretObjective not allowed in the current match state!");
+ }
+
+ /**
+ * Checks dynamically if a player can choose their secret objective in the current state, otherwise throws an exception.
+ * The check is performed implicitly, since this version of the method is called if and only if it hasn't been
+ * overridden by the MatchState object from which it's called.
+ *
+ * @throws WrongStateException if the method cannot be called in the current state
+ */
+ public void chooseSecretObjective() throws WrongStateException {
+ throw new WrongStateException("chooseSecretObjective not allowed in the current match state!");
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.html
new file mode 100644
index 00000000..16032de2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.html
@@ -0,0 +1 @@
+NextTurnState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.java.html
new file mode 100644
index 00000000..1f4f354a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/NextTurnState.java.html
@@ -0,0 +1,81 @@
+NextTurnState.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.WrongStateException;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match decides whether it must give initial cards, give
+ * secret objectives, or wait for someone to play a card, according to the current match flow.
+ */
+public class NextTurnState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public NextTurnState(Match match) {
+ super(match);
+ match.nextPlayer();
+ }
+
+ /**
+ * This method call is allowed by this class instances if and only if the match hasn't started yet
+ * and the initial card side choosing has already finished.
+ *
+ * @throws WrongStateException If called when the match has already started or the initial card side choosing
+ * hasn't finished yet.
+ */
+ @Override
+ public void proposeSecretObjectives() throws WrongStateException {
+ if (match.isStarted() || !match.isInitialTurnFinished())
+ throw new WrongStateException("proposeSecretObjectives called after the match was started");
+ }
+
+ /**
+ * This method call is allowed by this class instances if and only if the match has already started.
+ * @throws WrongStateException If called when the match hasn't started yet
+ */
+ @Override
+ public void makeMove() throws WrongStateException {
+ if (!match.isStarted())
+ throw new WrongStateException("makeMove called when match was not started yet");
+ }
+
+ /**
+ * This method call is allowed by this class instances if and only if the initial card side choosing
+ * hasn't finished yet.
+ * @throws WrongStateException If called when the initial card side choosing has already finished.
+ */
+ @Override
+ public void drawInitialCard() throws WrongStateException {
+ if (match.isInitialTurnFinished())
+ throw new WrongStateException("drawInitialCard called after the initial turn was finished");
+ }
+
+ /**
+ * Transitions to:
+ * - {@link AfterMoveState} if the match has already started;
+ * - {@link ChooseInitialSideState} if the initial card side choosing hasn't finished yet;
+ * - {@link ChooseSecretObjectiveState} if the match hasn't started yet and the initial card side choosing has
+ * already finished;
+ */
+ @Override
+ public void transition() {
+ MatchState nextState;
+
+ if (match.isStarted())
+ nextState = new AfterMoveState(match);
+ else if (!match.isInitialTurnFinished())
+ nextState = new ChooseInitialSideState(match);
+ else
+ nextState = new ChooseSecretObjectiveState(match);
+
+ match.setState(nextState);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.html
new file mode 100644
index 00000000..79f564d8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.html
@@ -0,0 +1 @@
+Objective
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.java.html
new file mode 100644
index 00000000..40718cc7
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Objective.java.html
@@ -0,0 +1,70 @@
+Objective.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Every player has a secret objective, and at the start of the game two objectives common to every
+ * player get randomly chosen. The objective asks for a certain requirement to be satisfied and gives points only when
+ * the game ends, and does not stack on itself (e.g. if an objective requires three feathers and a player has 6, he will receive the points only once)
+ */
+public class Objective implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private int points;
+ private Requirement req;
+ private static Integer lastID = 0;
+ private Integer id;
+
+ /**
+ * Class constructor. It is composed only of a requirement (using polymorphism) and the points it gives
+ *
+ * @param points the number of points the objective will give (which is always an absolute number, it never depends on any resource)
+ * @param req the requirement to satisfy in order to receive the points
+ */
+ public Objective(int points, Requirement req) {
+ Objective.lastID++;
+ this.id = Objective.lastID;
+ this.points = points;
+ this.req = req;
+ }
+
+ /**
+ * Getter for the Objective class
+ *
+ * @return the points held by the objective card.
+ */
+ public int getPoints() {
+ return this.points;
+ }
+
+ /**
+ * Getter for the Objective class
+ *
+ * @return the requirement for the objective card to be placed.
+ */
+ public Requirement getReq() {
+ return this.req;
+ }
+
+ /**
+ * @return the id of the objective card
+ */
+ public Integer getID() {
+ return this.id;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof Objective))
+ return false;
+
+ Objective other = (Objective) obj;
+ return other.getID().equals(this.id);
+ }
+}
+
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.html
new file mode 100644
index 00000000..7c4e5fec
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.html
@@ -0,0 +1 @@
+PlacedCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.java.html
new file mode 100644
index 00000000..fe807b81
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacedCard.java.html
@@ -0,0 +1,68 @@
+PlacedCard.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * This class handles the card already placed on the board, since we need to remember which card covers which
+ */
+public class PlacedCard implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Card card;
+ private int turn;
+ private Side playedSide;
+
+ /**
+ * Class constructor, which only needs to initialize the card and the turn it is played, as well as the side it was played on
+ *
+ * @param card the {@link Card} played
+ * @param playedSide the side the card was played on
+ * @param turn the turn said card is played. Needed to know which card covers which, since a card played in a certain turn will
+ * always cover one played before
+ */
+ public PlacedCard(Card card, Side playedSide, int turn) {
+ this.card = card;
+ this.turn = turn;
+ this.playedSide = playedSide;
+ }
+
+ /**
+ * Getter for the PlacedCard class
+ *
+ * @return the card just placed.
+ */
+ public Card getCard() {
+ return this.card;
+ }
+
+ /**
+ * Getter for the PlacedCard class
+ *
+ * @return turn in which the card was just placed.
+ */
+ public int getTurn() {
+ return this.turn;
+ }
+
+ /**
+ * Getter for the PlacedCard class
+ *
+ * @return side the card was played
+ */
+ public Side getPlayedSide() {
+ return this.playedSide;
+ }
+
+
+ /**
+ * Getter for the Card face
+ *
+ * @return the topological description of the side the card was played on
+ */
+ public CardFace getPlayedCardFace() {
+ return this.card.getSide(this.playedSide);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.html
new file mode 100644
index 00000000..e788754e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.html
@@ -0,0 +1 @@
+PlacementOutcome
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.java.html
new file mode 100644
index 00000000..ffa35cb8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlacementOutcome.java.html
@@ -0,0 +1,14 @@
+PlacementOutcome.java
package it.polimi.ingsw.gamemodel;
+
+/**
+ * Before placing a card, its placement must be verified. This enum is used to know if it is a valid one and, if it isn't, what went wrong
+ *
+ * @see Board#verifyCardPlacement(it.polimi.ingsw.utils.Pair, Card, Side)
+ */
+public enum PlacementOutcome {
+ VALID,
+ INVALID_COORDS,
+ INVALID_ENOUGH_RESOURCES
+}
+
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.html
new file mode 100644
index 00000000..f1b6018f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.html
@@ -0,0 +1 @@
+PlayableCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.java.html
new file mode 100644
index 00000000..eb694c90
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PlayableCard.java.html
@@ -0,0 +1,62 @@
+PlayableCard.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.InvalidResourceException;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Class that represents every kind of card that can be played during the game.
+ * All these cards have at least a side (the back) that does not require any
+ * resource to be played.
+ *
+ * @see CardFace
+ */
+public abstract sealed class PlayableCard extends Card implements Serializable permits GoldCard, ResourceCard {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ protected int points;
+ protected Symbol reign;
+ private static Integer lastID = 0;
+
+ /**
+ * Constructor for PlayableCard. Since the only common aspects are that they
+ * have a reign and that the back is made up of
+ * only full corners (no resources) and the center gives a resource of their
+ * reign
+ *
+ * @param reign the reign of the card
+ * @throws InvalidResourceException if the passed resource is not in
+ * {@link Symbol#getReigns()}
+ */
+ public PlayableCard(Symbol reign) throws InvalidResourceException {
+ PlayableCard.lastID++;
+ this.id = PlayableCard.lastID;
+ if (Symbol.getReigns().contains(reign)) {
+ this.points = 0;
+ this.reign = reign;
+ this.back = new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER,
+ Set.of(reign));
+ } else {
+ throw new InvalidResourceException(
+ "Resource " + reign.toString() + " is not valid for a " + this.getClass());
+ }
+ }
+
+ public PlayableCard() {
+
+ }
+
+ /**
+ * Getter for the card reign
+ *
+ * @return the card's reign
+ * @see Symbol#getReigns()
+ */
+ public Symbol getReign() {
+ return this.reign;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.html
new file mode 100644
index 00000000..17b2a582
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.html
@@ -0,0 +1 @@
+Player
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.java.html
new file mode 100644
index 00000000..72fa8a11
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Player.java.html
@@ -0,0 +1,268 @@
+Player.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.HandException;
+import it.polimi.ingsw.exceptions.WrongChoiceException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.exceptions.WrongTurnException;
+import it.polimi.ingsw.utils.Pair;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Represents each in-game user, so acts also as a gateway receiving input by the Controller.
+ * It's also responsible for the board's logic, which is a slice of the game logic.
+ */
+public class Player implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final String username;
+ private final Match match;
+ private int points;
+ private final Board board;
+ private Color pawnColor;
+ private Objective secretObjective;
+ /**
+ * If the player is connected
+ */
+ private boolean connected;
+
+ /**
+ * Initializes the main player's attributes.
+ *
+ * @param username the player's username
+ * @param match the match the player belongs to
+ */
+ public Player(String username, Match match) {
+ this.username = username;
+ this.match = match;
+
+ this.connected = true;
+ //Initialize values
+ board = new Board();
+ points = 0;
+ }
+
+ /**
+ * Initializes the current instance from a copy reference
+ *
+ * @param player The player to make a copy of
+ */
+ public Player(Player player) {
+ this.username = player.username;
+ this.match = player.match;
+ this.board = player.board;
+ this.points = player.points;
+ this.pawnColor = player.pawnColor;
+ this.secretObjective = player.secretObjective;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Player player)) return false;
+
+ return username.equals(player.username) &&
+ match.equals(player.match);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(username, match, points, board, pawnColor, secretObjective);
+ }
+
+ /**
+ * Places a card on the player's board, on the give side and in the given position, assuming it's valid.
+ *
+ * @param coords x and y position in which the card is played (where 0, 0 is the initial card)
+ * @param card the card to be placed
+ * @param side whether the card should be placed on the front or on the back
+ * @throws WrongChoiceException If the chosen card cannot be player (placement not allowed, or not enough resources,
+ * or card not in the player's hand)
+ * @throws WrongStateException If a card cannot be played in this match state
+ * @throws WrongTurnException If it's the turn of this player.
+ */
+ public void playCard(Pair<Integer, Integer> coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this))
+ match.makeMove(coords, card, side);
+ else
+ throw new WrongTurnException("Only the current player can play cards");
+ }
+ }
+
+ /**
+ * Gets two objectives from the match objectives deck considered to be secret.
+ *
+ * @return a pair of objectives
+ * @throws WrongStateException if called during the wrong match state
+ * @throws WrongTurnException if called by the player when it's not its turn
+ */
+ public Pair<Objective, Objective> drawSecretObjectives() throws WrongStateException, WrongTurnException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this)) {
+ return match.proposeSecretObjectives();
+ } else {
+ throw new WrongTurnException("Only the current player can draw secret objectives");
+ }
+ }
+ }
+
+ /**
+ * Gets an initial card from the match.
+ *
+ * @return an initial card
+ * @throws WrongTurnException if called by the player when it's not its turn
+ * @throws WrongStateException if called during the wrong match state
+ */
+ public InitialCard drawInitialCard() throws WrongTurnException, WrongStateException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this))
+ return match.drawInitialCard();
+ else
+ throw new WrongTurnException("Only the current player can draw the initial card");
+ }
+ }
+
+ /**
+ * Chooses the initial card side.
+ *
+ * @param side the side of the initial card
+ * @throws WrongTurnException if called by the player when it's not its turn
+ * @throws WrongStateException if called during the wrong match state
+ */
+ public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this))
+ match.setInitialSide(side, board.getAvailableResources());
+ else
+ throw new WrongTurnException("Only the current player can choose the initial card side");
+ }
+ }
+
+ /**
+ * Adds a card to the player's hand, popping it from the required source
+ *
+ * @param source represents the source of the draw, which can be either one of the two decks or one of the four cards on the table
+ * @throws WrongTurnException if called by the player when it's not its turn
+ * @throws WrongChoiceException if called on a drawing source which is empty (e.g. empty deck)
+ * @throws WrongStateException if called during the wrong match state
+ * @throws HandException if the player already has three cards in their hand
+ */
+ public void drawCard(DrawSource source) throws HandException, WrongStateException, WrongChoiceException, WrongTurnException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this)) {
+ PlayableCard card = match.drawCard(source);
+ board.addHandCard(card);
+ } else {
+ throw new WrongTurnException("Only the current player can draw cards");
+ }
+ }
+ }
+
+ /**
+ * Sets the player private objective (only at the start of the game).
+ *
+ * @param objective the chosen objective between the two proposed
+ * @throws WrongTurnException if called by the player when it's not its turn
+ * @throws WrongStateException if called during the wrong match state
+ * @throws WrongChoiceException if called on an objective which is not one of the proposed ones
+ */
+ public void chooseSecretObjective(Objective objective) throws WrongTurnException, WrongStateException, WrongChoiceException {
+ synchronized (match) {
+ if (match.getCurrentPlayer().equals(this)) {
+ match.setSecretObjective(objective);
+ secretObjective = objective;
+ } else {
+ throw new WrongTurnException("Only the current player can choose an objective");
+ }
+ }
+ }
+
+ /**
+ * Sends a message in public chat
+ *
+ * @param text content of the message
+ */
+ public void sendBroadcastText(String text) {
+ this.match.sendBroadcastText(this, text);
+ }
+
+ /**
+ * Sends a private message to the specified recipient
+ *
+ * @param recipient recipient of the message
+ * @param text content of the message
+ */
+ public void sendPrivateText(Player recipient, String text) {
+ this.match.sendPrivateText(this, recipient, text);
+ }
+
+ /**
+ * Getter for the player's board.
+ */
+ public Board getBoard() {
+ return board;
+ }
+
+ /**
+ * Getter for the player's points.
+ */
+ public int getPoints() {
+ return points;
+ }
+
+ /**
+ * Getter for the player's pawn color.
+ */
+ public Color getPawnColor() {
+ return pawnColor;
+ }
+
+ /**
+ * Setter for the player's color.
+ */
+ protected void setColor(Color color) {
+ this.pawnColor = color;
+ }
+
+ /**
+ * Getter for the player's secret objective.
+ *
+ * @see #chooseSecretObjective(Objective)
+ */
+ public Objective getSecretObjective() {
+ return secretObjective;
+ }
+
+ /**
+ * Adds points to the player.
+ *
+ * @param points number of points to add to the player
+ */
+ protected void addPoints(int points) {
+ this.points += points;
+ }
+
+ /**
+ * Getter for the player's username.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ public boolean isConnected() {
+ synchronized (match) {
+ return connected;
+ }
+ }
+
+ public void setConnected(boolean connected) {
+ synchronized (match) {
+ this.connected = connected;
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.html
new file mode 100644
index 00000000..c54e692f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.html
@@ -0,0 +1 @@
+PositionRequirement
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.java.html
new file mode 100644
index 00000000..cc3ba1af
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/PositionRequirement.java.html
@@ -0,0 +1,93 @@
+PositionRequirement.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.exceptions.InvalidResourceException;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other
+ */
+public class PositionRequirement extends Requirement implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Map<Pair<Integer, Integer>, Symbol> reqs;
+
+ /**
+ * Note that, since this requirement only cares about relative positioning, there must always be
+ * an element whose key is (0, 0)
+ *
+ * @param reqs The relative positioning of the cards (of which we only care about the faction).
+ */
+ public PositionRequirement(Map<Pair<Integer, Integer>, Symbol> reqs) throws InvalidResourceException {
+ EnumSet<Symbol> validResources = Symbol.getReigns();
+ for (Symbol s : reqs.values()) {
+ if (!validResources.contains(s)) {
+ throw new InvalidResourceException("Resource " + s.toString() + " is not valid for a " + this.getClass().toString());
+ }
+ }
+
+ this.reqs = reqs;
+ }
+
+ /**
+ * The requirement will be satisfied if the board has cards of the specified faction in the correct relative positions
+ *
+ * @param board the {@link Board} on which the requirement must be checked
+ * @return whether the board actually meets the requirement or not
+ */
+ @Override
+ public int timesMet(Board board) {
+ Map<Pair<Integer, Integer>, PlacedCard> placedCards = board.getPlacedCards();
+ List<Pair<Integer, Integer>> alreadyUsed = new ArrayList<>();
+
+ PlacedCard cmpPlaced;
+ Card cmp;
+
+ boolean requirementMatched;
+ int timesMet = 0;
+
+ Pair<Integer, Integer> tmp;
+
+ for (Pair<Integer, Integer> coord : placedCards.keySet()) {
+ requirementMatched = true;
+ // for each card in the board
+ for (Pair<Integer, Integer> offset : this.reqs.keySet()) {
+ tmp = new Pair<>(coord.first() + offset.first(), coord.second() + offset.second());
+ cmpPlaced = placedCards.get(tmp);
+ if (cmpPlaced != null) { // card in required position does not exist so go next
+ cmp = cmpPlaced.getCard();
+ // if the card is not a playable it is the initial (so go next), and if the reign is not matched go next
+ if ((!(cmp instanceof PlayableCard)) || ((PlayableCard) cmp).getReign() != this.reqs.get(offset) || alreadyUsed.indexOf(tmp) != -1) {
+ requirementMatched = false;
+ }
+ } else {
+ requirementMatched = false;
+ }
+ }
+ if (requirementMatched) {
+ for (Pair<Integer, Integer> offset : reqs.keySet()) {
+ alreadyUsed.add(new Pair<>(coord.first() + offset.first(), coord.second() + offset.second()));
+ }
+ timesMet++;
+ }
+ }
+ return timesMet;
+ }
+
+ /**
+ * Getter for the PositionRequirement class
+ * @return the positioning requirement for the objective card to be activated
+ *
+ */
+ public Map<Pair<Integer, Integer>, Symbol> getReqs(){
+ return this.reqs;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.html
new file mode 100644
index 00000000..9e2dc49c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.html
@@ -0,0 +1 @@
+QuantityRequirement
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.java.html
new file mode 100644
index 00000000..32cf71e3
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/QuantityRequirement.java.html
@@ -0,0 +1,65 @@
+QuantityRequirement.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.EnumSet;
+import java.util.Map;
+import it.polimi.ingsw.exceptions.InvalidResourceException;
+
+/**
+ * This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other
+ */
+public class QuantityRequirement extends Requirement implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private Map<Symbol, Integer> reqs;
+
+ /**
+ * Class constructor. Only valid symbols are the ones returned by {@link Symbol#getBasicResources()}
+ *
+ * @param reqs how many resources of a certain type are needed to fulfill the requirement.
+ * @throws InvalidResourceException if a requirement is not made up only of those symbols
+ */
+ public QuantityRequirement(Map<Symbol, Integer> reqs) throws InvalidResourceException {
+ EnumSet<Symbol> validResources = Symbol.getBasicResources();
+ for (Symbol s : reqs.keySet()) {
+ if (!validResources.contains(s)) {
+ throw new InvalidResourceException("Resource " + s.toString() + " is not valid for a " + this.getClass().toString());
+ }
+ }
+ this.reqs = reqs;
+ }
+
+ /**
+ * The requirement will be satisfied if the board has enough resources of the specified type
+ *
+ * @param board the board on which we check the requirement
+ * @return whether the requirement is satisfied or not
+ */
+ @Override
+ public int timesMet(Board board) {
+ Map<Symbol, Integer> availableResources = board.getAvailableResources();
+ Integer min = null;
+ Integer curr;
+
+ for (Symbol req : reqs.keySet()) {
+ curr = availableResources.get(req) / reqs.get(req);
+ if (min == null || curr < min) {
+ min = curr;
+ }
+ }
+
+ return min;
+ }
+
+ /**
+ * Getter for the QuantityRequirement class
+ *
+ * @return Map representing the card's placement requirements
+ */
+ public Map<Symbol, Integer> getReqs() {
+ return this.reqs;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.html
new file mode 100644
index 00000000..07293268
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.html
@@ -0,0 +1 @@
+Requirement
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.java.html
new file mode 100644
index 00000000..f8a5051f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Requirement.java.html
@@ -0,0 +1,30 @@
+Requirement.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * A condition must be met in order to play a golden card and to get points from the objectives. Those requirements are both represented by this class
+ */
+public abstract class Requirement implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Empty class constructor. The actual constructor resides in the subclasses, but this method is used to know
+ * what to expect when creating a new Requirement, allowing the use of polymorphism
+ *
+ * @see Symbol
+ */
+ public Requirement() {
+ }
+
+ /**
+ * Will be implemented on the concrete classes, as they have different kind of conditions
+ *
+ * @param board the board that will be used to check if the requirement is met
+ * @return whether the requirement is met or not
+ */
+ public abstract int timesMet(Board board);
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.html
new file mode 100644
index 00000000..f2636a91
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.html
@@ -0,0 +1 @@
+ResourceCard
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.java.html
new file mode 100644
index 00000000..7a9a2915
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/ResourceCard.java.html
@@ -0,0 +1,38 @@
+ResourceCard.java
package it.polimi.ingsw.gamemodel;
+
+import it.polimi.ingsw.exceptions.InvalidResourceException;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Card that does not require any conditions to be played.
+ */
+public final class ResourceCard extends PlayableCard implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Class constructor. It needs to only take the front as an argument, since the back is handled by its superclass {@link PlayableCard}
+ *
+ * @param front the front side of the card
+ * @param reign the reign of the card
+ * @param points the number of points the card gives (must be an absolute value)
+ * @throws InvalidResourceException if the passed resource is not in {@link Symbol#getReigns()}
+ */
+ public ResourceCard(CardFace front, Symbol reign, int points) throws InvalidResourceException {
+ super(reign);
+ this.front = front;
+ this.points = points;
+ }
+
+ /**
+ * Getter for the ResourceCard class
+ *
+ * @return points held by the card
+ */
+ public int getPoints() {
+ return this.points;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.html
new file mode 100644
index 00000000..4c409137
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.html
@@ -0,0 +1 @@
+Side
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.java.html
new file mode 100644
index 00000000..f7c705f1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Side.java.html
@@ -0,0 +1,36 @@
+Side.java
package it.polimi.ingsw.gamemodel;
+
+/**
+ * Represents a card side.
+ */
+public enum Side {
+ FRONT("front"),
+ BACK("back");
+
+ private String string;
+
+ Side(String string) {
+ this.string = string;
+ }
+
+ @Override
+ public String toString() {
+ return this.string;
+ }
+
+ /**
+ * Return a instance of this enum from its corresponding string representation.
+ *
+ * @param sideString The side represented as a string
+ * @return The side instance
+ */
+ public Side fromString(String sideString) {
+ for (Side side : Side.values()) {
+ if (side.toString().equals(sideString)) {
+ return side;
+ }
+ }
+ return null;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.html
new file mode 100644
index 00000000..5c6560f4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.html
@@ -0,0 +1 @@
+Symbol
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.java.html
new file mode 100644
index 00000000..3a148b10
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/Symbol.java.html
@@ -0,0 +1,95 @@
+Symbol.java
package it.polimi.ingsw.gamemodel;
+
+import java.util.EnumSet;
+
+/**
+ * Contains all the possible symbols a corner can store or a multiplier can have.
+ * Besides the basic 4 factions and 3 symbols, there is also EMPTY_CORNER, which represents a corner without the
+ * possibility of placing another card on top of it (missing slot), FULL_CORNER which represents a valid corner without any symbol and
+ * CORNER_OBJ which represents a {@link QuantityRequirement} in which the multiplier is how many corners the card covered
+ */
+public enum Symbol {
+ ANIMAL,
+ PLANT,
+ INSECT,
+ FUNGUS,
+ FEATHER,
+ INKWELL,
+ PARCHMENT,
+ EMPTY_CORNER,
+ FULL_CORNER,
+ NO_MULT,
+ CORNER_OBJ;
+
+ /**
+ * Generates subset containing only the four basic reigns
+ *
+ * @return the subset containing only the four basic reigns
+ */
+ static public EnumSet<Symbol> getReigns() {
+ return EnumSet.of(
+ ANIMAL,
+ PLANT,
+ INSECT,
+ FUNGUS
+ );
+ }
+
+ /**
+ * Generates subset containing only the basic resources (4 reigns and 3 "symbols")
+ *
+ * @return the subset containing only the basic resources (4 reigns and 3 "symbols")
+ */
+ static public EnumSet<Symbol> getBasicResources() {
+ return EnumSet.of(
+ ANIMAL,
+ PLANT,
+ INSECT,
+ FUNGUS,
+ FEATHER,
+ INKWELL,
+ PARCHMENT
+ );
+ }
+
+ /**
+ * Generates subset containing all the valid Symbols a corner can contain
+ *
+ * @return the subset containing all the valid Symbols a corner can contain
+ */
+ static public EnumSet<Symbol> getValidCorner() {
+ return EnumSet.of(
+ ANIMAL,
+ PLANT,
+ INSECT,
+ FUNGUS,
+ FEATHER,
+ INKWELL,
+ PARCHMENT,
+ EMPTY_CORNER,
+ FULL_CORNER
+ );
+ }
+
+ /**
+ * Generates subset containing all the symbols a {@link GoldCard} multiplier can be
+ *
+ * @return the subset containing all the symbols a {@link GoldCard} multiplier can be
+ */
+ static public EnumSet<Symbol> getValidMultiplier() {
+ return EnumSet.of(
+ ANIMAL,
+ PLANT,
+ INSECT,
+ FUNGUS,
+ FEATHER,
+ INKWELL,
+ PARCHMENT,
+ NO_MULT,
+ CORNER_OBJ
+ );
+ }
+
+}
+
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.html
new file mode 100644
index 00000000..ebe72001
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.html
@@ -0,0 +1 @@
+WaitState
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.java.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.java.html
new file mode 100644
index 00000000..56c5c613
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/WaitState.java.html
@@ -0,0 +1,57 @@
+WaitState.java
package it.polimi.ingsw.gamemodel;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Subclass of {@link MatchState}. This is the state in which the match is when accepting new players or them leaving,
+ * that is to say: before the match is full and so it starts.
+ */
+public class WaitState extends MatchState implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Initializes this instance.
+ *
+ * @param match The match in this state
+ */
+ public WaitState(Match match) {
+ super(match);
+ }
+
+ /**
+ * Transitions to {@link NextTurnState}.
+ */
+ @Override
+ public void transition() {
+ synchronized (match) {
+ if (match.isFull()) {
+ match.setupDecks();
+ match.setupPlayers();
+ match.setupBoards();
+
+ MatchState nextState = new NextTurnState(match);
+ match.setState(nextState);
+
+ // Notify observers
+ match.notifyMatchStart();
+ }
+ }
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void removePlayer() {
+ }
+
+ /**
+ * This method call is allowed by this class instances.
+ */
+ @Override
+ public void addPlayer() {
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/index.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/index.html
new file mode 100644
index 00000000..d3aea692
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.gamemodel
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.gamemodel/index.source.html b/deliveries/Test results/it.polimi.ingsw.gamemodel/index.source.html
new file mode 100644
index 00000000..be7dc67e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.gamemodel/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.gamemodel
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.html
new file mode 100644
index 00000000..b61ead65
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.html
@@ -0,0 +1 @@
+ActionMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.java.html
new file mode 100644
index 00000000..f69fafcf
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ActionMessage.java.html
@@ -0,0 +1,42 @@
+ActionMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+import it.polimi.ingsw.network.messages.Message;
+
+/**
+ * Messages sent by clients to the server to express a user intention to do an
+ * action
+ */
+public sealed abstract class ActionMessage extends Message permits ChooseInitialCardSideMessage, ChooseSecretObjectiveMessage,
+ CreateMatchMessage, DrawCardMessage, DrawInitialCardMessage, DrawSecretObjectivesMessage, GetAvailableMatchesMessage,
+ JoinMatchMessage, PlayCardMessage, SendBroadcastTextMessage, SendPrivateTextMessage {
+ private String action;
+ private String username;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param username The player who wants to perform the action
+ */
+ public ActionMessage(String username) {
+ super();
+ this.action = this.getClass().getSimpleName().replace("Message", "");
+ this.username = username;
+ }
+
+ /**
+ * @return name of the action
+ */
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * @return username of the player
+ */
+ public String getUsername() {
+ return username;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.html
new file mode 100644
index 00000000..4ec4a3bb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.html
@@ -0,0 +1 @@
+ChooseInitialCardSideMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.java.html
new file mode 100644
index 00000000..f9243dce
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseInitialCardSideMessage.java.html
@@ -0,0 +1,31 @@
+ChooseInitialCardSideMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+import it.polimi.ingsw.gamemodel.Side;
+
+/**
+ * The action communicates the player's choice of the initial card's side. It can only happen before the first turn of the game.
+ * If the action is successful, {@link it.polimi.ingsw.network.messages.responses.SomeoneDrewCardMessage} response is sent to every client.
+ */
+public final class ChooseInitialCardSideMessage extends ActionMessage {
+ private Side side;
+
+ /**
+ * @return chosen side
+ */
+ public Side getSide() {
+ return side;
+ }
+
+
+ /**
+ * Class constructor.
+ *
+ * @param username The username of the player that wants to choose the initial card side
+ * @param side The chosen card side
+ */
+ public ChooseInitialCardSideMessage(String username, Side side) {
+ super(username);
+ this.side = side;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.html
new file mode 100644
index 00000000..ff23ad9b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.html
@@ -0,0 +1 @@
+ChooseSecretObjectiveMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.java.html
new file mode 100644
index 00000000..ae6e919c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/ChooseSecretObjectiveMessage.java.html
@@ -0,0 +1,30 @@
+ChooseSecretObjectiveMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * The action communicates the intention of a player to choose his secret objective. It can only happen before the first turn of the game.
+ * If the action is successful, a {@link it.polimi.ingsw.network.messages.responses.SomeoneChoseSecretObjectiveMessage} response is sent to every client.
+ */
+public final class ChooseSecretObjectiveMessage extends ActionMessage {
+ private Integer objectiveID;
+
+ /**
+ * @return Chosen objective
+ */
+ public Integer getObjectiveID() {
+ return objectiveID;
+ }
+
+
+ /**
+ * Class constructor.
+ *
+ * @param username The username of the player that wants to choose secret objective
+ * @param objectiveID The chosen objective's ID
+ */
+ public ChooseSecretObjectiveMessage(String username, Integer objectiveID) {
+ super(username);
+ this.objectiveID = objectiveID;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.html
new file mode 100644
index 00000000..b814ca7a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.html
@@ -0,0 +1 @@
+CreateMatchMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.java.html
new file mode 100644
index 00000000..6eace6c1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/CreateMatchMessage.java.html
@@ -0,0 +1,32 @@
+CreateMatchMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * The action communicates (to the server) the intention of a client to create a new match.
+ */
+public final class CreateMatchMessage extends ActionMessage {
+ private final String matchName;
+ private final int maxPlayers;
+
+ public CreateMatchMessage(String username, String matchName, int maxPlayers) {
+ super(username);
+ this.matchName = matchName;
+ this.maxPlayers = maxPlayers;
+ }
+
+ /***
+ *
+ * @return Name of the match
+ */
+ public String getMatchName() {
+ return matchName;
+ }
+
+ /***
+ *
+ * @return Number of maximum players (must be between 2 and 4)
+ */
+ public int getMaxPlayers() {
+ return maxPlayers;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.html
new file mode 100644
index 00000000..da56bd96
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.html
@@ -0,0 +1 @@
+DrawCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.java.html
new file mode 100644
index 00000000..dee66c67
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawCardMessage.java.html
@@ -0,0 +1,24 @@
+DrawCardMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+import it.polimi.ingsw.gamemodel.DrawSource;
+
+/**
+ * The action communicates the intention of a player to draw a card. It can only happen during the player's own turn.
+ * If the action is successful, a {@link it.polimi.ingsw.network.messages.responses.SomeoneDrewInitialCardMessage} response is sent to every client.
+ */
+public final class DrawCardMessage extends ActionMessage {
+ private DrawSource source;
+
+ /**
+ * @return chosen source for the draw
+ */
+ public DrawSource getSource() {
+ return source;
+ }
+
+ public DrawCardMessage(String username, DrawSource source) {
+ super(username);
+ this.source = source;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.html
new file mode 100644
index 00000000..8f79fc8c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.html
@@ -0,0 +1 @@
+DrawInitialCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.java.html
new file mode 100644
index 00000000..b1e351cb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawInitialCardMessage.java.html
@@ -0,0 +1,13 @@
+DrawInitialCardMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * It communicates the intention of a player to draw the initial card. It can only happen before the first turn of the game.
+ * - If the action is successful, a {@link it.polimi.ingsw.network.messages.responses.SomeoneDrewInitialCardMessage} response is sent to every client.
+ */
+public final class DrawInitialCardMessage extends ActionMessage {
+ public DrawInitialCardMessage(String username) {
+ super(username);
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.html
new file mode 100644
index 00000000..83245260
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.html
@@ -0,0 +1 @@
+DrawSecretObjectivesMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.java.html
new file mode 100644
index 00000000..1587b973
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/DrawSecretObjectivesMessage.java.html
@@ -0,0 +1,13 @@
+DrawSecretObjectivesMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * It communicates the intention of a player to draw the (2) secret objectives. It can only happen before the first turn of the game.
+ * If the action is successful, a {@link it.polimi.ingsw.network.messages.responses.SomeoneDrewSecretObjectivesMessage} response is sent to every client.
+ */
+public final class DrawSecretObjectivesMessage extends ActionMessage {
+
+ public DrawSecretObjectivesMessage(String username) {
+ super(username);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.html
new file mode 100644
index 00000000..7a7d1185
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.html
@@ -0,0 +1 @@
+GetAvailableMatchesMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.java.html
new file mode 100644
index 00000000..94788f19
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/GetAvailableMatchesMessage.java.html
@@ -0,0 +1,12 @@
+GetAvailableMatchesMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * The client asks for an updated version of the lobby;
+ * The server returns an {@link it.polimi.ingsw.network.messages.responses.AvailableMatchesMessage} response.
+ */
+public final class GetAvailableMatchesMessage extends ActionMessage {
+ public GetAvailableMatchesMessage(String username) {
+ super(username);
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.html
new file mode 100644
index 00000000..2a827c86
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.html
@@ -0,0 +1 @@
+JoinMatchMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.java.html
new file mode 100644
index 00000000..d26f2f82
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/JoinMatchMessage.java.html
@@ -0,0 +1,21 @@
+JoinMatchMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+/**
+ * The action communicates the intention of a client to join a match.
+ */
+public final class JoinMatchMessage extends ActionMessage {
+ private final String matchName;
+
+ public JoinMatchMessage(String username, String matchName) {
+ super(username);
+ this.matchName = matchName;
+ }
+
+ /**
+ * @return Name of the match to join
+ */
+ public String getMatchName() {
+ return matchName;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.html
new file mode 100644
index 00000000..8a1bdab5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.html
@@ -0,0 +1 @@
+PlayCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.java.html
new file mode 100644
index 00000000..1ea1ca6f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/PlayCardMessage.java.html
@@ -0,0 +1,53 @@
+PlayCardMessage.java
package it.polimi.ingsw.network.messages.actions;
+
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * The action communicates the intention of the player to place a card on its board. It can only happen during the player's own turn.
+ * If the action is successful, a {@link it.polimi.ingsw.network.messages.responses.SomeonePlayedCardMessage} response is sent to every client.
+ */
+public final class PlayCardMessage extends ActionMessage {
+ private final Integer x, y;
+ private final Integer cardID;
+ private final Side side;
+
+ /**
+ * @return x coordinate of the played card
+ */
+ public Integer getX() {
+ return x;
+ }
+
+ /**
+ * @return y coordinate of the played card
+ */
+ public Integer getY() {
+ return y;
+ }
+
+ /**
+ * @return id of the played card
+ */
+ public Integer getCardID() {
+ return cardID;
+ }
+
+ /**
+ * @return side of the played card
+ */
+ public Side getSide() {
+ return side;
+ }
+
+
+ public PlayCardMessage(String username, Pair<Integer, Integer> coords, Integer cardID, Side side) {
+ super(username);
+ this.x = coords.first();
+ this.y = coords.second();
+ this.cardID = cardID;
+ this.side = side;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.html
new file mode 100644
index 00000000..00851cc5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.html
@@ -0,0 +1 @@
+SendBroadcastTextMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.java.html
new file mode 100644
index 00000000..66b58726
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendBroadcastTextMessage.java.html
@@ -0,0 +1,19 @@
+SendBroadcastTextMessage.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.html
new file mode 100644
index 00000000..aa256e42
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.html
@@ -0,0 +1 @@
+SendPrivateTextMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.java.html
new file mode 100644
index 00000000..72a94d31
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/SendPrivateTextMessage.java.html
@@ -0,0 +1,25 @@
+SendPrivateTextMessage.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.html
new file mode 100644
index 00000000..60a03f3b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.actions
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.source.html b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.source.html
new file mode 100644
index 00000000..c845e8bd
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.actions/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.actions
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.html
new file mode 100644
index 00000000..265243b1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.html
@@ -0,0 +1 @@
+ErrorMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.java.html
new file mode 100644
index 00000000..2b5938ca
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/ErrorMessage.java.html
@@ -0,0 +1,33 @@
+ErrorMessage.java
package it.polimi.ingsw.network.messages.errors;
+
+import it.polimi.ingsw.network.messages.Message;
+
+/**
+ * Sent to the clients when an error happens
+ */
+public class ErrorMessage extends Message {
+ private final String message;
+ private final String error;
+
+ public ErrorMessage(String message, String error) {
+ this.message = message;
+ this.error = error;
+ }
+
+ /**
+ * Getter for the error code of the message
+ * @return error code of the message
+ */
+ public String getError() {
+ return error;
+ }
+
+ /**
+ * Getter for the message in human language
+ * @return the mesage
+ */
+ public String getMessage() {
+ return message;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.html b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.html
new file mode 100644
index 00000000..9bf49f8b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.errors
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.source.html b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.source.html
new file mode 100644
index 00000000..a7aebc16
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.errors/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.errors
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.html
new file mode 100644
index 00000000..dfa42c94
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.html
@@ -0,0 +1 @@
+AvailableMatchesMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.java.html
new file mode 100644
index 00000000..e6754a14
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/AvailableMatchesMessage.java.html
@@ -0,0 +1,42 @@
+AvailableMatchesMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.gamemodel.Match;
+import it.polimi.ingsw.utils.AvailableMatch;
+
+/**
+ * This response is sent when a user is connected to the server.
+ */
+public final class AvailableMatchesMessage extends ResponseMessage {
+
+ private final List<AvailableMatch> matches;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param availableMatches The available matches present on server
+ */
+ public AvailableMatchesMessage(Map<String, Match> availableMatches) {
+ super(null);
+ matches = new ArrayList<>();
+ availableMatches.forEach((n, m) -> matches.add(encodeMatch(n, m)));
+ }
+
+ /**
+ * @return a list containing a JsonObject for each match with properties:
+ * name - name of the match
+ * maxPlayers - maximum number of players
+ * joinedPlayers - number of players in the match
+ */
+ public List<AvailableMatch> getMatches() {
+ return matches;
+ }
+
+ private final AvailableMatch encodeMatch(String name, Match match) {
+ return new AvailableMatch(name, match.getMaxPlayers(), match.getPlayers().size(), match.isRejoinable());
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.html
new file mode 100644
index 00000000..5c892273
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.html
@@ -0,0 +1 @@
+MatchFinishedMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.java.html
new file mode 100644
index 00000000..e08be001
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchFinishedMessage.java.html
@@ -0,0 +1,35 @@
+MatchFinishedMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import it.polimi.ingsw.gamemodel.Player;
+import it.polimi.ingsw.utils.LeaderboardEntry;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * This response is sent to each player when the match is finished
+ */
+public final class MatchFinishedMessage extends ResponseMessage {
+ private final List<LeaderboardEntry> ranking;
+
+ /**
+ * @return a list of JSONObject with properties
+ * username (String) - username of the current player
+ * points (Integer) - total number of points gained during the match
+ * winner (boolean) - if the current player is also the winner of the game
+ */
+ public List<LeaderboardEntry> getRanking() {
+ return ranking;
+ }
+
+ public MatchFinishedMessage(List<Pair<Player, Boolean>> finalRanking) {
+ super(null);
+ ranking = finalRanking.stream().map(p -> createLeaderboardEntry(p.first(), p.second())).collect(Collectors.toList());
+ }
+
+ private LeaderboardEntry createLeaderboardEntry(Player p, Boolean b) {
+ return new LeaderboardEntry(p.getUsername(), p.getPoints(), b);
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.html
new file mode 100644
index 00000000..18125f8c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.html
@@ -0,0 +1 @@
+MatchResumedMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.java.html
new file mode 100644
index 00000000..3fde9c1c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchResumedMessage.java.html
@@ -0,0 +1,126 @@
+MatchResumedMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import java.util.List;
+import java.util.Map;
+import it.polimi.ingsw.gamemodel.Color;
+import it.polimi.ingsw.gamemodel.DrawSource;
+import it.polimi.ingsw.gamemodel.Symbol;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.PlacedCardRecord;
+
+/**
+ * This response is sent to the user who just rejoined a match. All the parameters refer to the
+ * status of the match before the server crashed
+ */
+public final class MatchResumedMessage extends ResponseMessage {
+ private final Map<String, Color> playersUsernamesAndPawns;
+ private final Map<String, List<Integer>> playersHands;
+ private final Pair<Integer, Integer> visibleObjectives;
+ private final Map<DrawSource, Integer> visiblePlayableCards;
+ private final Pair<Symbol, Symbol> decksTopReigns;
+ private final Integer secretObjective;
+ private final Map<String, Map<Symbol, Integer>> availableResources;
+ private final Map<String, Map<Integer, PlacedCardRecord>> placedCards;
+ private final Map<String, Integer> playerPoints;
+ private final String currentPlayer;
+ private final boolean drawPhase;
+
+ public MatchResumedMessage(Map<String, Color> playersUsernamesAndPawns,
+ Map<String, List<Integer>> playersHands, Pair<Integer, Integer> visibleObjectives,
+ Map<DrawSource, Integer> visiblePlayableCards, Pair<Symbol, Symbol> decksTopReigns,
+ Integer secretObjective, Map<String, Map<Symbol, Integer>> availableResources,
+ Map<String, Map<Integer, PlacedCardRecord>> placedCards,
+ Map<String, Integer> playerPoints, String currentPlayer, boolean drawPhase) {
+ super(null);
+ this.playersUsernamesAndPawns = playersUsernamesAndPawns;
+ this.playersHands = playersHands;
+ this.visibleObjectives = visibleObjectives;
+ this.visiblePlayableCards = visiblePlayableCards;
+ this.decksTopReigns = decksTopReigns;
+ this.secretObjective = secretObjective;
+ this.availableResources = availableResources;
+ this.placedCards = placedCards;
+ this.playerPoints = playerPoints;
+ this.currentPlayer = currentPlayer;
+ this.drawPhase = drawPhase;
+ }
+
+
+ /**
+ * @return A map from players' username to pawn color
+ */
+ public Map<String, Color> getPlayersUsernamesAndPawns() {
+ return playersUsernamesAndPawns;
+ }
+
+ /**
+ * @return A map from players' username to their hand
+ */
+ public Map<String, List<Integer>> getPlayersHands() {
+ return playersHands;
+ }
+
+ /**
+ * @return The two visible objectives common to every player
+ */
+ public Pair<Integer, Integer> getVisibleObjectives() {
+ return visibleObjectives;
+ }
+
+ /**
+ * @return The four drawable cards visible to everyone
+ */
+ public Map<DrawSource, Integer> getVisiblePlayableCards() {
+ return visiblePlayableCards;
+ }
+
+ /**
+ * @return The reign of the two decks (resource and gold)
+ */
+ public Pair<Symbol, Symbol> getDecksTopReigns() {
+ return decksTopReigns;
+ }
+
+ /**
+ * @return The secret objective ID of the player
+ */
+ public Integer getSecretObjective() {
+ return secretObjective;
+ }
+
+ /**
+ * @return A map from players' username to their available resources
+ */
+ public Map<String, Map<Symbol, Integer>> getAvailableResources() {
+ return availableResources;
+ }
+
+ /**
+ * @return A map from players' username to their board
+ */
+ public Map<String, Map<Integer, PlacedCardRecord>> getPlacedCards() {
+ return placedCards;
+ }
+
+ /**
+ * @return A map from players' username to their points
+ */
+ public Map<String, Integer> getPlayerPoints() {
+ return playerPoints;
+ }
+
+ /**
+ * @return Username of the player currently playing his turn
+ */
+ public String getCurrentPlayer() {
+ return currentPlayer;
+ }
+
+ /**
+ * @return Whether the current player should play or draw a card
+ */
+ public boolean isDrawPhase() {
+ return drawPhase;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.html
new file mode 100644
index 00000000..76924528
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.html
@@ -0,0 +1 @@
+MatchStartedMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.java.html
new file mode 100644
index 00000000..59a75363
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/MatchStartedMessage.java.html
@@ -0,0 +1,90 @@
+MatchStartedMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.Pair;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Sent when the required amount of players is reached and the match is about to start.
+ */
+public final class MatchStartedMessage extends ResponseMessage {
+ private final Integer[] visibleObjectives;
+ private final Map<DrawSource, Integer> visibleCards;
+ private final Symbol[] visibleDeckReigns;
+ private final Map<String, Integer[]> playerHands;
+ private final Map<String, Color> playerPawnColors;
+
+ /**
+ * @return IDs of the visible objectives
+ */
+ public Integer[] getVisibleObjectives() {
+ return visibleObjectives;
+ }
+
+ /**
+ * @return a Map that maps to each visible draw source the ID of the card
+ */
+ public Map<DrawSource, Integer> getVisibleCards() {
+ return visibleCards;
+ }
+
+ /**
+ * @return Array of Symbol that contains the reign of top-card
+ * of both the gold and the resource deck, in the first and second slot respectively
+ */
+ public Symbol[] getVisibleDeckReigns() {
+ return visibleDeckReigns;
+ }
+
+ /**
+ * @return Map mapping to each player username, the list of the cards they have in the hand
+ */
+ public Map<String, Integer[]> getPlayerHands() {
+ return playerHands;
+ }
+
+ /**
+ * @return Map containing for each palyer username, the Color of their pawn
+ */
+ public Map<String, Color> getPlayerPawnColors() {
+ return playerPawnColors;
+ }
+
+
+ /**
+ * Calculates the needed parameters given some information from the match
+ *
+ * @param objectives Pair containing the two visible objectives
+ * @param cards Map that for each visible draw source maps the visible card
+ * @param deckReigns Pair containing the reign of the two visible cards on top of the deck.
+ * The first is for the Golds deck, while the second for the resources deck.
+ * @param players List of the players in the match
+ */
+ public MatchStartedMessage(Pair<Objective, Objective> objectives, Map<DrawSource, PlayableCard> cards, Pair<Symbol, Symbol> deckReigns, List<Player> players) {
+ super(null);
+ this.visibleObjectives = new Integer[]{null, null};
+ this.visibleDeckReigns = new Symbol[]{null, null};
+ visibleObjectives[0] = objectives.first().getID();
+ visibleObjectives[1] = objectives.second().getID();
+ visibleCards = new HashMap<>();
+ cards.forEach((d, c) -> visibleCards.put(d, c.getId()));
+ visibleDeckReigns[0] = deckReigns.first();
+ visibleDeckReigns[1] = deckReigns.second();
+ // Calculate player hands and colors
+ playerHands = new HashMap<String, Integer[]>();
+ playerPawnColors = new HashMap<String, Color>();
+ for (Player p : players) {
+ Integer[] result;
+ result = p.getBoard().getCurrentHand().stream()
+ .mapToInt(Card::getId)
+ .boxed().toArray(Integer[]::new);
+ playerPawnColors.put(p.getUsername(), p.getPawnColor());
+ playerHands.put(p.getUsername(), result);
+ }
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.html
new file mode 100644
index 00000000..130ca0b4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.html
@@ -0,0 +1 @@
+ResponseMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.java.html
new file mode 100644
index 00000000..020567f4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/ResponseMessage.java.html
@@ -0,0 +1,36 @@
+ResponseMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import it.polimi.ingsw.network.messages.Message;
+
+/**
+ * Messages sent from the server to the clients to update them about another
+ * user's move or to the consequence of their action
+ */
+public sealed class ResponseMessage extends Message permits AvailableMatchesMessage, MatchFinishedMessage, MatchStartedMessage,
+ SomeoneJoinedMessage, SomeoneQuitMessage, SomeoneChoseSecretObjectiveMessage, SomeoneDrewCardMessage, SomeoneDrewInitialCardMessage,
+ SomeoneDrewSecretObjectivesMessage, SomeonePlayedCardMessage, SomeoneSetInitialSideMessage,
+ SomeoneSentBroadcastTextMessage, SomeoneSentPrivateTextMessage, MatchResumedMessage {
+ private final String username;
+ private final String response = this.getClass().getSimpleName().replace("Message", "");
+
+ /**
+ * @return username of the user that did the action
+ * null if not specified by the protocol
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * @return repsonse type
+ */
+ public String getResponse() {
+ return response;
+ }
+
+ public ResponseMessage(String username) {
+ this.username = username;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.html
new file mode 100644
index 00000000..56c20ca8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.html
@@ -0,0 +1 @@
+SomeoneChoseSecretObjectiveMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.java.html
new file mode 100644
index 00000000..56e67083
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneChoseSecretObjectiveMessage.java.html
@@ -0,0 +1,22 @@
+SomeoneChoseSecretObjectiveMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+/**
+ * This response is sent to each user in the match when a user chooses his secret objective.
+ */
+public final class SomeoneChoseSecretObjectiveMessage extends ResponseMessage {
+ private final Integer objectiveID;
+
+ /**
+ * @return ID of the chosen objective. Is null if the player it is sent to not the current player
+ */
+ public Integer getObjectiveID() {
+ return objectiveID;
+ }
+
+ public SomeoneChoseSecretObjectiveMessage(String username, Integer objectiveID) {
+ super(username);
+ this.objectiveID = objectiveID;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.html
new file mode 100644
index 00000000..c196d82c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.html
@@ -0,0 +1 @@
+SomeoneDrewCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.java.html
new file mode 100644
index 00000000..3536addf
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewCardMessage.java.html
@@ -0,0 +1,59 @@
+SomeoneDrewCardMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import it.polimi.ingsw.gamemodel.DrawSource;
+import it.polimi.ingsw.gamemodel.Symbol;
+import it.polimi.ingsw.utils.Pair;
+
+import javax.swing.plaf.synth.SynthButtonUI;
+
+/**
+ * This response is sent to each user in the match when a user draws a card.
+ */
+public final class SomeoneDrewCardMessage extends ResponseMessage {
+ private final DrawSource drawSource;
+ private final Integer cardID;
+ private final Integer replacementCardID;
+ private final Pair<Symbol, Symbol> deckTopReigns;
+
+ /**
+ * @return Source from which the card is drawn.
+ */
+ public DrawSource getDrawSource() {
+ return drawSource;
+ }
+
+ /**
+ * @return ID of the card drawn by the player
+ */
+ public Integer getCardID() {
+ return cardID;
+ }
+
+ /**
+ * @return ID of the card that replaced the drawn card. Not available if the source is GOLDS_DECK or RESOURCES_DECK
+ */
+ public Integer getReplacementCardID() {
+ return replacementCardID;
+ }
+
+ /**
+ * @return Reign of the replaced card
+ */
+ public Pair<Symbol, Symbol> getDeckTopReigns() {
+ return deckTopReigns;
+ }
+
+ public SomeoneDrewCardMessage(String username, DrawSource source, Integer cardID, Integer replacementCardID, Pair<Symbol, Symbol> deckTopReigns) {
+ super(username);
+ this.drawSource = source;
+ this.cardID = cardID;
+ this.deckTopReigns = deckTopReigns;
+ if (!source.equals(DrawSource.GOLDS_DECK) && !source.equals(DrawSource.RESOURCES_DECK)) {
+ this.replacementCardID = replacementCardID;
+ } else {
+ this.replacementCardID = null;
+ }
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.html
new file mode 100644
index 00000000..fb6ffd84
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.html
@@ -0,0 +1 @@
+SomeoneDrewInitialCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.java.html
new file mode 100644
index 00000000..a452989d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewInitialCardMessage.java.html
@@ -0,0 +1,20 @@
+SomeoneDrewInitialCardMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+/**
+ * This response is sent to each user in the match when a user draws an initial card.
+ */
+public final class SomeoneDrewInitialCardMessage extends ResponseMessage {
+ private final Integer initialCardID;
+
+ public SomeoneDrewInitialCardMessage(String username, Integer initialCardID) {
+ super(username);
+ this.initialCardID = initialCardID;
+ }
+
+ /* ID of the given initial card */
+ public Integer getInitialCardID() {
+ return initialCardID;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.html
new file mode 100644
index 00000000..fbff7850
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.html
@@ -0,0 +1 @@
+SomeoneDrewSecretObjectivesMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.java.html
new file mode 100644
index 00000000..5498a063
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneDrewSecretObjectivesMessage.java.html
@@ -0,0 +1,31 @@
+SomeoneDrewSecretObjectivesMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * This response is sent to each user in the match when a user draws the two secret objectives.
+ */
+public final class SomeoneDrewSecretObjectivesMessage extends ResponseMessage {
+ private final Integer firstID, secondID;
+
+ /**
+ * @return ID of the first objective card drawn. Is null if the player it is sent to not the current player
+ */
+ public Integer getFirstID() {
+ return firstID;
+ }
+
+ /**
+ * @return ID of the second objective card drawn. Is null if the player it is sent to not the current player
+ */
+ public Integer getSecondID() {
+ return secondID;
+ }
+
+ public SomeoneDrewSecretObjectivesMessage(String username, Pair<Integer, Integer> IDs) {
+ super(username);
+ this.firstID = IDs.first();
+ this.secondID = IDs.second();
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.html
new file mode 100644
index 00000000..a1318659
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.html
@@ -0,0 +1 @@
+SomeoneJoinedMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.java.html
new file mode 100644
index 00000000..a7e3f543
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneJoinedMessage.java.html
@@ -0,0 +1,34 @@
+SomeoneJoinedMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import it.polimi.ingsw.gamemodel.Player;
+
+import java.util.List;
+
+/**
+ * This response is sent when a player joins the current match.
+ */
+public final class SomeoneJoinedMessage extends ResponseMessage {
+ private final List<String> joinedPlayers;
+ private final int maxPlayers;
+
+ public SomeoneJoinedMessage(String username, List<Player> joinedPlayers, int maxPlayers) {
+ super(username);
+ this.joinedPlayers = joinedPlayers.stream().map(Player::getUsername).toList();
+ this.maxPlayers = maxPlayers;
+ }
+
+ /**
+ * @return Usernames of players currently in the match
+ */
+ public List<String> getJoinedPlayers() {
+ return joinedPlayers;
+ }
+
+ /**
+ * @return Maximum amount of players the match can hold
+ */
+ public int getMaxPlayers() {
+ return maxPlayers;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.html
new file mode 100644
index 00000000..3a8b64bb
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.html
@@ -0,0 +1 @@
+SomeonePlayedCardMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.java.html
new file mode 100644
index 00000000..44131a91
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeonePlayedCardMessage.java.html
@@ -0,0 +1,71 @@
+SomeonePlayedCardMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import java.util.Map;
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.gamemodel.Symbol;
+import it.polimi.ingsw.utils.Pair;
+
+/**
+ * SomeonePlayedCardMessage
+ */
+public final class SomeonePlayedCardMessage extends ResponseMessage {
+ private final Integer x, y;
+ private final Integer cardID;
+ private final Side side;
+ private final Integer points;
+ private final Map<Symbol, Integer> availableResources;
+
+ /**
+ * @return x coordinate of the played card
+ */
+ public Integer getX() {
+ return x;
+ }
+
+ /**
+ * @return y coordinate of the played card
+ */
+ public Integer getY() {
+ return y;
+ }
+
+ /**
+ * @return ID of the played card
+ */
+ public Integer getCardID() {
+ return cardID;
+ }
+
+ /**
+ * @return Side in which the card has been played. It can be either FRONT or BACK
+ */
+ public Side getSide() {
+ return side;
+ }
+
+ /**
+ * @return Total points of the player after he placed the card
+ */
+ public Integer getPoints() {
+ return points;
+ }
+
+ /**
+ * @return Resources possessed by the player after he placed the card
+ */
+ public Map<Symbol, Integer> getAvailableResources() {
+ return availableResources;
+ }
+
+ public SomeonePlayedCardMessage(String username, Pair<Integer, Integer> coords, Integer cardID, Side side, int points, Map<Symbol, Integer> availableResources) {
+ super(username);
+ this.x = coords.first();
+ this.y = coords.second();
+ this.cardID = cardID;
+ this.side = side;
+ this.points = points;
+ this.availableResources = availableResources;
+ }
+
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.html
new file mode 100644
index 00000000..7a4ae522
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.html
@@ -0,0 +1 @@
+SomeoneQuitMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.java.html
new file mode 100644
index 00000000..a3f4ebcf
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneQuitMessage.java.html
@@ -0,0 +1,30 @@
+SomeoneQuitMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+/**
+ * This response is sent when a player quits the current match.
+ */
+public final class SomeoneQuitMessage extends ResponseMessage {
+ private final int joinedPlayers;
+ private final boolean endMatch;
+
+ public SomeoneQuitMessage(String username, int joinedPlayers, boolean endMatch) {
+ super(username);
+ this.joinedPlayers = joinedPlayers;
+ this.endMatch = endMatch;
+ }
+
+ /**
+ * @return Username of the player that just quit the match
+ */
+ public int getJoinedPlayers() {
+ return joinedPlayers;
+ }
+
+ /**
+ * @return true if the quit caused the match to interrupt, false otherwise
+ */
+ public boolean isEndMatch() {
+ return endMatch;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.html
new file mode 100644
index 00000000..1de98e11
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.html
@@ -0,0 +1 @@
+SomeoneSentBroadcastTextMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.java.html
new file mode 100644
index 00000000..d57c4821
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentBroadcastTextMessage.java.html
@@ -0,0 +1,19 @@
+SomeoneSentBroadcastTextMessage.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.html
new file mode 100644
index 00000000..d50e4af9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.html
@@ -0,0 +1 @@
+SomeoneSentPrivateTextMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.java.html
new file mode 100644
index 00000000..4f2eb823
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSentPrivateTextMessage.java.html
@@ -0,0 +1,25 @@
+SomeoneSentPrivateTextMessage.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.html
new file mode 100644
index 00000000..780bc6ce
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.html
@@ -0,0 +1 @@
+SomeoneSetInitialSideMessage
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.java.html
new file mode 100644
index 00000000..58554c67
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/SomeoneSetInitialSideMessage.java.html
@@ -0,0 +1,34 @@
+SomeoneSetInitialSideMessage.java
package it.polimi.ingsw.network.messages.responses;
+
+import java.util.Map;
+import it.polimi.ingsw.gamemodel.Side;
+import it.polimi.ingsw.gamemodel.Symbol;
+
+/**
+ * This response is sent to each user in the match when a user chosees the initial side of a card.
+ */
+public final class SomeoneSetInitialSideMessage extends ResponseMessage {
+ private final Side side;
+ private final Map<Symbol, Integer> availableResources;
+
+ /**
+ * @return Available resources of player after setting the initial card
+ */
+ public Map<Symbol, Integer> getAvailableResources() {
+ return availableResources;
+ }
+
+ /**
+ * @return Side of the initial card.
+ */
+ public Side getSide() {
+ return side;
+ }
+
+ public SomeoneSetInitialSideMessage(String username, Side side, Map<Symbol, Integer> availableResources) {
+ super(username);
+ this.side = side;
+ this.availableResources = availableResources;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.html
new file mode 100644
index 00000000..336d2e73
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.responses
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.source.html b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.source.html
new file mode 100644
index 00000000..29f8efb3
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages.responses/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages.responses
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages/Message.html b/deliveries/Test results/it.polimi.ingsw.network.messages/Message.html
new file mode 100644
index 00000000..c8a49602
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages/Message.html
@@ -0,0 +1 @@
+Message
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages/Message.java.html b/deliveries/Test results/it.polimi.ingsw.network.messages/Message.java.html
new file mode 100644
index 00000000..e943a1d4
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages/Message.java.html
@@ -0,0 +1,8 @@
+Message.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages/index.html b/deliveries/Test results/it.polimi.ingsw.network.messages/index.html
new file mode 100644
index 00000000..b1975172
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.messages/index.source.html b/deliveries/Test results/it.polimi.ingsw.network.messages/index.source.html
new file mode 100644
index 00000000..712272d5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.messages/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.messages
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.html
new file mode 100644
index 00000000..774c62c1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.html
@@ -0,0 +1 @@
+ClientListener
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.java.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.java.html
new file mode 100644
index 00000000..48302e3d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientListener.java.html
@@ -0,0 +1,268 @@
+ClientListener.java
package it.polimi.ingsw.network.tcp;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Map;
+import com.google.gson.JsonParseException;
+import it.polimi.ingsw.controllers.PlayerControllerTCP;
+import it.polimi.ingsw.exceptions.AlreadyUsedUsernameException;
+import it.polimi.ingsw.exceptions.ChosenMatchException;
+import it.polimi.ingsw.exceptions.WrongNameException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.network.messages.actions.*;
+import it.polimi.ingsw.network.messages.errors.ErrorMessage;
+import it.polimi.ingsw.network.messages.responses.AvailableMatchesMessage;
+import it.polimi.ingsw.network.messages.responses.ResponseMessage;
+import it.polimi.ingsw.server.Server;
+import it.polimi.ingsw.utils.CardsManager;
+import it.polimi.ingsw.utils.MessageJsonParser;
+import it.polimi.ingsw.utils.Pair;
+
+/*
+ * actual connection procedure: - socket accepted - socket asks for available matches, giving its
+ * name to server - when received, it communicates which match it wants to join - only then a
+ * PlayerController will be created, with said match - from there the constructor is done, a player
+ * has joined and it just has to listen
+ */
+
+/**
+ * Every time a socket gets accepted by the TCP server, a new ClientListener will be created with
+ * it, and it will:
+ * <ul>
+ * <li>Acquire the client's username
+ * <li>Make the client (which is still not a {@link Player}) choose/create a {@link Match} to join
+ * <li>Create its {@link PlayerControllerTCP}, which will also make him join such {@link Match}
+ * <li>Listen for any message received and, execute the corresponding action.
+ * </ul>
+ *
+ * Note that this will just require the action to be executed, but it's {@link PlayerControllerTCP}
+ * that actually calls the {@link Player} methods
+ */
+public class ClientListener extends Thread {
+ private Socket socket;
+ private PlayerControllerTCP playerController;
+ private MessageJsonParser parser;
+ private IOHandler io;
+ private Server server;
+ private Match match;
+ private Map<Integer, Objective> objectives;
+ private Map<Integer, PlayableCard> playableCards;
+
+ /**
+ * Class constructor. Needs to have a reference to the server instance since it needs to handle
+ * the match assignment
+ *
+ * @param socket the socket that required a connection
+ * @param server the instance of {@link Server} that's running
+ */
+ public ClientListener(Socket socket, Server server) {
+ try {
+ this.socket = socket;
+ this.io = new IOHandler(this.socket);
+ this.server = server;
+ this.parser = new MessageJsonParser();
+
+ this.objectives = CardsManager.getInstance().getObjectives();
+ Map<Integer, ResourceCard> resources = CardsManager.getInstance().getResourceCards();
+ Map<Integer, GoldCard> golds = CardsManager.getInstance().getGoldCards();
+
+ this.playableCards = new HashMap<>();
+ resources.forEach((id, card) -> this.playableCards.put(id, card));
+ golds.forEach((id, card) -> this.playableCards.put(id, card));
+
+ } catch (IOException e) {
+ this.sendError("Failed to create Listener thread", e);
+ }
+ }
+
+
+ /**
+ * Sends error message with custom text
+ *
+ * @param prompt the text to be shown
+ * @param exception the exception type
+ */
+ private void sendError(String prompt, Exception exception) {
+ try {
+ this.io.writeMsg(new ErrorMessage(prompt, exception.getClass().getName()));
+ } catch (Exception ignored) {
+ }
+ }
+
+
+ /**
+ * Loops until a player controller is created
+ *
+ * @throws IOException if there was an I/O error
+ */
+ private void setPlayerController() {
+ ActionMessage msg;
+ String username = null;
+ Match match = null;
+ ResponseMessage availableMatches;
+ boolean shouldLoop = true;
+
+ while (shouldLoop) {
+ try {
+ msg = (ActionMessage) this.parser.toMessage(this.io.readMsg());
+ if (msg == null) {
+ shouldLoop = false;
+ } else {
+ switch (msg) {
+ case GetAvailableMatchesMessage getAvailableMatchesMessage:
+ username = getAvailableMatchesMessage.getUsername();
+ availableMatches = new AvailableMatchesMessage(
+ this.server.getJoinableMatchesMap());
+ this.io.writeMsg(availableMatches);
+ break;
+
+ case CreateMatchMessage createMatchMessage:
+ username = createMatchMessage.getUsername();
+ this.server.createMatch(createMatchMessage.getMatchName(),
+ createMatchMessage.getMaxPlayers());
+ match = this.server.getMatch(createMatchMessage.getMatchName());
+
+ this.createPlayerController(username, match);
+ shouldLoop = false;
+ break;
+
+ case JoinMatchMessage joinMatchMessage:
+ username = joinMatchMessage.getUsername();
+ match = this.server.getMatch(joinMatchMessage.getMatchName());
+
+ this.createPlayerController(username, match);
+ shouldLoop = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (JsonParseException | ClassNotFoundException e) {
+ // message is not correctly formatted, ignore
+ } catch (ChosenMatchException | WrongStateException | AlreadyUsedUsernameException
+ | IllegalArgumentException | WrongNameException e) {
+ this.sendError(e.getMessage(), e);
+ } catch (IOException e) {
+ this.close(match);
+ }
+ }
+ this.match = match;
+ }
+
+
+ /**
+ * Tries to actually create the player controller with the acquired information
+ *
+ * @param username The chosen username
+ * @param match The match to join
+ * @throws AlreadyUsedUsernameException If the match already contains the chosen username
+ * @throws WrongStateException If the match currently does not accept new players
+ * @throws ChosenMatchException If the match does not exist or is not valid
+ */
+ private void createPlayerController(String username, Match match)
+ throws AlreadyUsedUsernameException, IllegalArgumentException, WrongStateException,
+ ChosenMatchException, WrongNameException {
+ this.playerController = new PlayerControllerTCP(username, match, this.io);
+ this.playerController.sendJoined();
+ }
+
+ /**
+ * This parses the message received from socket's input stream and executes the request such
+ * message carried. If the message is not one of the expected types, it will just be ignored
+ *
+ * @param msg The received message (received as a string)
+ *
+ * @see ActionMessage
+ */
+ private void executeRequest(String msg) {
+ try {
+
+ ActionMessage message = (ActionMessage) parser.toMessage(msg);
+ if (msg != null) {
+ switch (message) {
+ case ChooseSecretObjectiveMessage actionMsg:
+ this.playerController.chooseSecretObjective(
+ this.objectives.get(actionMsg.getObjectiveID()));
+ break;
+ case ChooseInitialCardSideMessage actionMsg:
+ this.playerController.chooseInitialCardSide(actionMsg.getSide());
+ break;
+ case DrawCardMessage actionMsg:
+ this.playerController.drawCard(actionMsg.getSource());
+ break;
+ case DrawInitialCardMessage actionMsg:
+ this.playerController.drawInitialCard();
+ break;
+ case DrawSecretObjectivesMessage actionMsg:
+ this.playerController.drawSecretObjectives();
+ break;
+ case SendBroadcastTextMessage actionMsg:
+ this.playerController.sendBroadcastText(actionMsg.getText());
+ break;
+ case SendPrivateTextMessage actionMsg:
+ this.playerController.sendPrivateText(actionMsg.getRecipient(),
+ actionMsg.getText());
+ break;
+ case PlayCardMessage actionMsg:
+ Pair<Integer, Integer> coords =
+ new Pair<>(actionMsg.getX(), actionMsg.getY());
+ PlayableCard card = this.playableCards.get(actionMsg.getCardID());
+ this.playerController.playCard(coords, card, actionMsg.getSide());
+ break;
+ default:
+ break;
+ }
+ }
+ } catch (JsonParseException e) {
+ // Nothing to do here: it was either a ping or a wrongly formatted message
+ }
+
+ }
+
+ /**
+ * Main loop. This will just wait for anything to be put on the input stream and then call
+ * {@link ClientListener#executeRequest(String)}
+ */
+ public void listen() {
+ try {
+ while (!this.socket.isClosed() && this.socket.isConnected()) {
+ String msg = this.io.readMsg();
+ // if msg is null, it means the socket was closed client side. Quit all
+ if (msg == null) {
+ throw new IOException("Socket closed");
+ }
+ this.executeRequest(msg);
+ }
+ } catch (IOException | ClassNotFoundException e) {
+ this.close(match);
+ }
+ }
+
+ /**
+ * This will close socket and input/output handlers, if not null
+ */
+ private void close(Match match) {
+ try {
+ match.removePlayer(this.playerController.getPlayer());
+ if (this.socket != null && !this.socket.isClosed()) {
+ this.io.close();
+ this.socket.close();
+ }
+ } catch (IOException | NullPointerException e) {
+ }
+ }
+
+ /**
+ * Since the class extends {@link Thread} it needs to implement {@link Thread#run()}.
+ * Specifically, this will just run {@link ClientListener#listen()}
+ */
+ @Override
+ public void run() {
+ this.setPlayerController();
+ this.listen();
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.html
new file mode 100644
index 00000000..b17e0679
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.html
@@ -0,0 +1 @@
+ClientReceiver
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.java.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.java.html
new file mode 100644
index 00000000..7efda5e0
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/ClientReceiver.java.html
@@ -0,0 +1,275 @@
+ClientReceiver.java
package it.polimi.ingsw.network.tcp;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import it.polimi.ingsw.client.frontend.GraphicalView;
+import it.polimi.ingsw.client.network.NetworkHandlerTCP;
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.network.messages.errors.ErrorMessage;
+import it.polimi.ingsw.network.messages.responses.*;
+import it.polimi.ingsw.utils.CardsManager;
+import it.polimi.ingsw.utils.Pair;
+import it.polimi.ingsw.utils.PlacedCardRecord;
+
+/**
+ * Receives messages from server to client
+ */
+public class ClientReceiver implements Runnable {
+ private NetworkHandlerTCP networkHandler;
+ private Socket socket;
+ private IOHandler io;
+ private Map<Integer, InitialCard> initialCards;
+ private Map<Integer, ResourceCard> resourceCards;
+ private Map<Integer, GoldCard> goldCards;
+ private Map<Integer, Objective> objectives;
+
+
+ /**
+ * Class constructor.
+ *
+ * @param networkHandler The network view that should call the {@link GraphicalView} methods
+ * @param socket The socket opened
+ *
+ * @throws IOException If there was an error with the socket's streams
+ */
+ public ClientReceiver(NetworkHandlerTCP networkHandler, Socket socket) throws IOException {
+ this.networkHandler = networkHandler;
+ this.socket = socket;
+ this.io = new IOHandler(socket);
+ this.io = networkHandler.getIO();
+
+ CardsManager cardsManager = CardsManager.getInstance();
+ this.initialCards = cardsManager.getInitialCards();
+ this.resourceCards = cardsManager.getResourceCards();
+ this.goldCards = cardsManager.getGoldCards();
+ this.objectives = cardsManager.getObjectives();
+ }
+
+
+ /**
+ * Get a playable card by its ID.
+ *
+ * @param cardID The card ID
+ *
+ * @return The playable card
+ */
+ private PlayableCard getPlayable(Integer cardID) {
+ PlayableCard card = this.resourceCards.get(cardID);
+ if (card == null) {
+ return this.goldCards.get(cardID);
+ }
+ return card;
+ }
+
+ /**
+ * Parses a map from coordinates to {@link PlacedCard} to a map from coordinates to
+ * {@link PlacedCardRecord}.
+ *
+ * @param board The map from coordinates to {@link PlacedCard}
+ *
+ * @return The map from coordinates to {@link PlacedCardRecord}
+ */
+ private Map<Pair<Integer, Integer>, PlacedCard> getPlacedMap(
+ Map<Integer, PlacedCardRecord> board) {
+ Map<Pair<Integer, Integer>, PlacedCard> result = new HashMap<>();
+
+ board.forEach((turn, placedCardRecord) -> {
+ if (placedCardRecord.x().equals(0) && placedCardRecord.y().equals(0)) {
+ result.put(new Pair<Integer, Integer>(placedCardRecord.x(), placedCardRecord.y()),
+ new PlacedCard(this.initialCards.get(placedCardRecord.cardID()),
+ placedCardRecord.side(), turn));
+ } else {
+ result.put(new Pair<Integer, Integer>(placedCardRecord.x(), placedCardRecord.y()),
+ new PlacedCard(this.getPlayable(placedCardRecord.cardID()),
+ placedCardRecord.side(), turn));
+ }
+ });
+
+ return result;
+ }
+
+
+ /**
+ * Parses the message and resumes the match.
+ *
+ * @param msg The message containing the match status
+ *
+ * @throws IOException if there was a problem with the socket stream
+ */
+ private void resumeMatch(MatchResumedMessage msg) throws IOException {
+ Map<String, Color> playersUsernamesAndPawns = new HashMap<>();
+ Map<String, List<PlayableCard>> playersHands = new HashMap<>();
+ Pair<Objective, Objective> visibleObjectives;
+ Map<DrawSource, PlayableCard> visiblePlayableCards = new HashMap<>();
+ Pair<Symbol, Symbol> decksTopReigns;
+ Objective secretObjective;
+ Map<String, Map<Symbol, Integer>> availableResources = new HashMap<>();
+ Map<String, Map<Pair<Integer, Integer>, PlacedCard>> placedCards = new HashMap<>();
+ Map<String, Integer> playerPoints = new HashMap<>();
+ String currentPlayer;
+ boolean drawPhase;
+
+ playersUsernamesAndPawns = msg.getPlayersUsernamesAndPawns();
+ playersHands = msg.getPlayersHands().entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream()
+ .map(cardID -> this.getPlayable(cardID)).collect(Collectors.toList())));
+ Pair<Integer, Integer> visibleObjectivesID = msg.getVisibleObjectives();
+ visibleObjectives =
+ new Pair<Objective, Objective>(this.objectives.get(visibleObjectivesID.first()),
+ this.objectives.get(visibleObjectivesID.second()));
+ visiblePlayableCards = msg.getVisiblePlayableCards().entrySet().stream().collect(
+ Collectors.toMap(Map.Entry::getKey, entry -> this.getPlayable(entry.getValue())));
+ decksTopReigns = msg.getDecksTopReigns();
+ secretObjective = this.objectives.get(msg.getSecretObjective());
+ availableResources = msg.getAvailableResources();
+ msg.getPlacedCards()
+ .forEach((player, board) -> placedCards.put(player, this.getPlacedMap(board)));
+ playerPoints = msg.getPlayerPoints();
+ currentPlayer = msg.getCurrentPlayer();
+ drawPhase = msg.isDrawPhase();
+
+ this.networkHandler.matchResumed(playersUsernamesAndPawns, playersHands, visibleObjectives,
+ visiblePlayableCards, decksTopReigns, secretObjective, availableResources,
+ placedCards, playerPoints, currentPlayer, drawPhase);
+ }
+
+ /**
+ * Parses a message and calls the corresponding
+ * {@link it.polimi.ingsw.client.network.NetworkHandler}'s view.
+ *
+ * @param message The message to be parsed
+ */
+ private void parseMessage(String message) {
+ try {
+ ResponseMessage response = (ResponseMessage) io.stringToMsg(message);
+ String username = response.getUsername();
+ switch (response) {
+ case AvailableMatchesMessage msg:
+ this.networkHandler.receiveAvailableMatches(msg.getMatches());
+ break;
+ case MatchResumedMessage msg:
+ this.resumeMatch(msg);
+ break;
+ case MatchStartedMessage msg:
+ Map<String, List<PlayableCard>> hands = new HashMap<>();
+ msg.getPlayerHands()
+ .forEach((player, hand) -> hands.put(player,
+ List.of(hand).stream().map(card -> this.getPlayable(card))
+ .collect(Collectors.toList())));
+
+ Pair<Objective, Objective> objectives = new Pair<Objective, Objective>(
+ this.objectives.get(msg.getVisibleObjectives()[0]),
+ this.objectives.get(msg.getVisibleObjectives()[1]));
+
+ Map<DrawSource, PlayableCard> visibles = new HashMap<>();
+ msg.getVisibleCards().forEach(
+ (source, card) -> visibles.put(source, this.getPlayable(card)));
+
+ Pair<Symbol, Symbol> decksTop = new Pair<Symbol, Symbol>(
+ msg.getVisibleDeckReigns()[0], msg.getVisibleDeckReigns()[1]);
+
+ this.networkHandler.matchStarted(msg.getPlayerPawnColors(), hands, objectives,
+ visibles, decksTop);
+ break;
+ case SomeoneDrewInitialCardMessage msg:
+ if (username.equals(this.networkHandler.getUsername())) {
+ this.networkHandler
+ .giveInitialCard(this.initialCards.get(msg.getInitialCardID()));
+ } else {
+ this.networkHandler.someoneDrewInitialCard(username,
+ this.initialCards.get(msg.getInitialCardID()));
+ }
+ break;
+ case SomeoneDrewSecretObjectivesMessage msg:
+ if (username.equals(this.networkHandler.getUsername())) {
+ Pair<Objective, Objective> objs =
+ new Pair<>(this.objectives.get(msg.getFirstID()),
+ this.objectives.get(msg.getSecondID()));
+ this.networkHandler.giveSecretObjectives(objs);
+ } else {
+ this.networkHandler.someoneDrewSecretObjective(username);
+ }
+ break;
+ case SomeoneSetInitialSideMessage msg:
+ this.networkHandler.someoneSetInitialSide(username, msg.getSide(),
+ msg.getAvailableResources());
+ break;
+ case SomeoneChoseSecretObjectiveMessage msg:
+ this.networkHandler.someoneChoseSecretObjective(username);
+ break;
+ case SomeonePlayedCardMessage msg:
+ Pair<Integer, Integer> coords =
+ new Pair<Integer, Integer>(msg.getX(), msg.getY());
+ this.networkHandler.someonePlayedCard(username, coords,
+ this.getPlayable(msg.getCardID()), msg.getSide(), msg.getPoints(),
+ msg.getAvailableResources());
+ break;
+ case SomeoneDrewCardMessage msg:
+ this.networkHandler.someoneDrewCard(username, msg.getDrawSource(),
+ this.getPlayable(msg.getCardID()),
+ this.getPlayable(msg.getReplacementCardID()), msg.getDeckTopReigns());
+ break;
+ case SomeoneJoinedMessage msg:
+ this.networkHandler.someoneJoined(username, msg.getJoinedPlayers());
+ break;
+ case SomeoneQuitMessage msg:
+ this.networkHandler.someoneQuit(username);
+ break;
+ case MatchFinishedMessage msg:
+ this.networkHandler.matchFinished(msg.getRanking());
+ break;
+ case SomeoneSentBroadcastTextMessage msg:
+ this.networkHandler.someoneSentBroadcastText(username, msg.getText());
+ break;
+ case SomeoneSentPrivateTextMessage msg:
+ this.networkHandler.someoneSentPrivateText(username, msg.getText());
+ break;
+ default:
+ break;
+ }
+ } catch (Exception e) {
+ this.sendError(message);
+ }
+
+ }
+
+
+ /**
+ * Sends an error to the server.
+ *
+ * @param message The error message
+ */
+ private void sendError(String message) {
+ try {
+ ErrorMessage msg = (ErrorMessage) this.io.stringToMsg(message);
+ Exception exception = new Exception(msg.getMessage());
+ this.networkHandler.notifyError(exception);
+ } catch (Exception e) {
+ // Nothing to do, received an invalid object
+ }
+ }
+
+
+ /**
+ * Receives answers from the server and tries to parse it (in a new thread)
+ */
+ @Override
+ public void run() {
+ String message;
+ while (!this.socket.isClosed() && this.socket.isConnected()) {
+ try {
+ message = this.io.readMsg();
+ final String finalMessage = message;
+ new Thread(() -> {
+ this.parseMessage(finalMessage);
+ }).start();
+ } catch (IOException | ClassNotFoundException e) {
+ }
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.html
new file mode 100644
index 00000000..15a2ad86
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.html
@@ -0,0 +1 @@
+IOHandler
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.java.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.java.html
new file mode 100644
index 00000000..b359d9bc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/IOHandler.java.html
@@ -0,0 +1,110 @@
+IOHandler.java
package it.polimi.ingsw.network.tcp;
+
+import java.io.*;
+import java.net.Socket;
+import java.net.SocketException;
+import it.polimi.ingsw.network.messages.Message;
+import it.polimi.ingsw.utils.MessageJsonParser;
+
+/**
+ * This class will handle all the IO operations for a certain socket.
+ *
+ * @see ObjectInputStream
+ * @see ObjectOutputStream
+ * @see MessageJsonParser
+ */
+public class IOHandler {
+ private final BufferedReader inputReader;
+ private final BufferedWriter outputWriter;
+
+ private final MessageJsonParser parser;
+
+ private final Socket socket;
+
+ /**
+ * Class constructor. It takes a {@link Socket} as a parameter to open its
+ * {@link ObjectOutputStream} and {@link ObjectInputStream}.
+ */
+ public IOHandler(Socket socket) throws IOException {
+
+ this.socket = socket;
+ this.outputWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ this.inputReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ this.parser = new MessageJsonParser();
+ }
+
+ /**
+ * Acquires a {@link Message} from the socket's input stream.
+ *
+ * @return the string representing the message
+ * @throws IOException if the remote communication failed
+ * @throws ClassNotFoundException if the class of the received object could not be found
+ */
+ public String readMsg() throws IOException, ClassNotFoundException {
+ return this.inputReader.readLine();
+ }
+
+ /**
+ * Writes a {@link Message} to the socket's output stream.
+ *
+ * @param msg The (parsed) message to write
+ * @throws IOException if the remote communication failed
+ */
+ public void writeMsg(String msg) throws IOException {
+ this.outputWriter.write(msg);
+ this.outputWriter.newLine();
+ this.outputWriter.flush();
+ }
+
+ /**
+ * Writes a {@link Message} to the socket's output stream.
+ *
+ * @param msg The (not yet parsed) message to write
+ * @throws IOException if the remote communication failed
+ */
+ public void writeMsg(Message msg) throws IOException {
+ this.outputWriter.write(this.msgToString(msg));
+ this.outputWriter.newLine();
+ this.outputWriter.flush();
+ }
+
+ /**
+ * Converts a {@link Message} to its corresponding Json.
+ *
+ * @param msg The message to be parsed
+ * @return the corresponding Json
+ */
+ public String msgToString(Message msg) {
+ return this.parser.toJson(msg);
+ }
+
+ /**
+ * Converts a Json string to its corresponding {@link Message}.
+ *
+ * @param msg The Json to be parsed
+ * @return the corresponding object
+ */
+ public Message stringToMsg(String msg) {
+ return this.parser.toMessage(msg);
+ }
+
+ /**
+ * Closes the input and output streams, if not null.
+ *
+ * @throws IOException if the streams could not be accessed
+ */
+ public void close() throws IOException {
+ try {
+ if (this.inputReader != null) {
+ this.inputReader.close();
+ }
+ if (this.outputWriter != null) {
+ this.outputWriter.close();
+ }
+ } catch (SocketException e) {
+ // socket already closed, no need to do anything
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.html
new file mode 100644
index 00000000..b7019042
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.html
@@ -0,0 +1 @@
+TCPServer
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.java.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.java.html
new file mode 100644
index 00000000..b8695f7e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/TCPServer.java.html
@@ -0,0 +1,50 @@
+TCPServer.java
package it.polimi.ingsw.network.tcp;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import it.polimi.ingsw.gamemodel.Match;
+import it.polimi.ingsw.server.Server;
+
+/**
+ * Class containing the {@link ServerSocket}. This will just accept sockets and
+ * start the {@link ClientListener} with it
+ */
+public class TCPServer {
+ private ServerSocket serverSocketTCP;
+ private Server server;
+
+ /**
+ * Class constructor. It will open a {@link ServerSocket} on the specified port
+ *
+ * @param port the port on which the server should be started
+ * @param server the {@link Server} object that contains all the {@link Match}
+ * objects
+ */
+ public TCPServer(Integer port, Server server) {
+ try {
+ this.serverSocketTCP = new ServerSocket(port);
+ this.server = server;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Main loop. Until the {@link ServerSocket} is not closed, it will listen for
+ * any {@link Socket} that tries to connect and accept them. Finally, it will
+ * start a new {@link ClientListener} with it
+ */
+ public void listen() {
+ while (!this.serverSocketTCP.isClosed()) {
+ try {
+ Socket socket = this.serverSocketTCP.accept();
+ new ClientListener(socket, server).start();
+ } catch (IOException e) {
+ System.out.println("Failed to accept socket");
+ e.printStackTrace();
+ }
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/index.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/index.html
new file mode 100644
index 00000000..e43999e8
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.tcp
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.network.tcp/index.source.html b/deliveries/Test results/it.polimi.ingsw.network.tcp/index.source.html
new file mode 100644
index 00000000..3fb80cd5
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.network.tcp/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.network.tcp
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.html b/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.html
new file mode 100644
index 00000000..f9d6c55d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.html
@@ -0,0 +1 @@
+MatchStatusObserver
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.java.html b/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.java.html
new file mode 100644
index 00000000..f9d9e330
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/MatchStatusObserver.java.html
@@ -0,0 +1,151 @@
+MatchStatusObserver.java
package it.polimi.ingsw.server;
+
+import it.polimi.ingsw.gamemodel.*;
+import it.polimi.ingsw.utils.Pair;
+
+import java.io.*;
+import java.util.Map;
+
+/**
+ * Subclass of {@link MatchObserver} supposed to perform routine actions, which means actions that are to be
+ * performed when there's a state transition, BUT that are not related to a specific player.
+ * It's current main function is to serialize the match from which it receives notifications.
+ */
+public class MatchStatusObserver implements MatchObserver {
+ private final String matchName;
+ private final Map<String, Match> matches;
+
+ /**
+ * Initializes this instance main attributes.
+ *
+ * @param matchName The match name to which this instance is attached and listens to.
+ * @param matches All the matches of the server
+ */
+ public MatchStatusObserver(String matchName, Map<String, Match> matches) {
+ this.matchName = matchName;
+ this.matches = matches;
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void matchStarted() {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneJoined(Player someone) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneQuit(Player someone) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneDrewInitialCard(Player someone, InitialCard card) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneSetInitialSide(Player someone, Side side, Map<Symbol, Integer> availableResources) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneDrewSecretObjective(Player someone, Pair<Objective, Objective> objectives) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneChoseSecretObjective(Player someone, Objective objective) {
+ }
+
+ /**
+ * Serializes the match and saves it in the disk. This method parameters are not used.
+ *
+ * @param someone Not used by this method.
+ * @param coords Not used by this method.
+ * @param card Not used by this method.
+ * @param side Not used by this method.
+ */
+ @Override
+ public void someonePlayedCard(Player someone, Pair<Integer, Integer> coords, PlayableCard card, Side side) {
+ serializeMatch();
+ }
+
+ /**
+ * Serializes the match and saves it in the disk. This method parameters are not used.
+ *
+ * @param someone Not used by this method.
+ * @param card Not used by this method.
+ */
+ @Override
+ public void someoneDrewCard(Player someone, DrawSource source, PlayableCard card, PlayableCard replacementCard) {
+ serializeMatch();
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneSentBroadcastText(Player someone, String text) {
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void someoneSentPrivateText(Player someone, Player recipient, String text) {
+ }
+
+ /**
+ * Removes this match serialization file from the disk and removes this match instance from the list of matches
+ * available in the {@link Server}.
+ */
+ @Override
+ public void matchFinished() {
+ matches.remove(matchName);
+ removeSerializedMatch();
+ }
+
+ /**
+ * Utility method that serializes the match and saves it in the disk.
+ */
+ private void serializeMatch() {
+ try {
+ FileOutputStream fileOut = new FileOutputStream(matchName + ".match");
+ ObjectOutputStream out = new ObjectOutputStream(fileOut);
+ Match m = matches.get(matchName);
+ synchronized (m) {
+ out.writeObject(m);
+ out.close();
+ fileOut.close();
+ }
+ } catch (IOException e) {
+ System.err.println("The match \"" + matchName + "\" cannot be serialized due to I/O errors");
+ }
+ }
+
+ // Utility method that removes a serialized match from the disk.
+ private void removeSerializedMatch() {
+ File file = new File(matchName + ".match");
+ file.delete();
+ }
+}
+
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/Server.html b/deliveries/Test results/it.polimi.ingsw.server/Server.html
new file mode 100644
index 00000000..c70bb610
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/Server.html
@@ -0,0 +1 @@
+Server
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/Server.java.html b/deliveries/Test results/it.polimi.ingsw.server/Server.java.html
new file mode 100644
index 00000000..f493af69
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/Server.java.html
@@ -0,0 +1,257 @@
+Server.java
package it.polimi.ingsw.server;
+
+import it.polimi.ingsw.client.network.NetworkHandler;
+import it.polimi.ingsw.client.network.NetworkHandlerRMI;
+import it.polimi.ingsw.controllers.PlayerController;
+import it.polimi.ingsw.controllers.PlayerControllerRMI;
+import it.polimi.ingsw.controllers.PlayerControllerRMIInterface;
+import it.polimi.ingsw.exceptions.AlreadyUsedUsernameException;
+import it.polimi.ingsw.exceptions.ChosenMatchException;
+import it.polimi.ingsw.exceptions.WrongNameException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.gamemodel.Match;
+import it.polimi.ingsw.network.tcp.TCPServer;
+import it.polimi.ingsw.utils.AvailableMatch;
+import it.polimi.ingsw.utils.DeckCreator;
+import it.polimi.ingsw.utils.GuiUtil;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The server class of this application. It's appointed with managing remote interactions with clients
+ * ({@link NetworkHandler}) before the match starts, after that {@link PlayerController} will ensure the
+ * communication.
+ * To be specific, it stores all the {@link Match} instances available (not full) or being played at the moment,
+ * creates them when requested by clients and restores them from disk (since periodically serialized) after a
+ * Server crash.
+ */
+public class Server extends UnicastRemoteObject implements ServerRMIInterface {
+ private final Map<String, Match> matches;
+ private final int portRMI;
+ private final int portTCP;
+
+ /**
+ * Initializes this Server instance and its attributes.
+ *
+ * @param portRMI The RMI port to listen to
+ * @param portTCP The TCP port to listen to
+ * @throws RemoteException If this instance couldn't be exported on the public RMI registry, so there's been a
+ * connection error
+ */
+ public Server(int portRMI, int portTCP) throws RemoteException {
+ super();
+
+ this.portRMI = portRMI;
+ this.portTCP = portTCP;
+
+ matches = new HashMap<>();
+ }
+
+ /**
+ * Returns the available matches as {@link AvailableMatch} instances.
+ * This method is called just by remote {@link NetworkHandlerRMI} instances.
+ *
+ * @return The list of Match which are not full yet.
+ */
+ @Override
+ public List<AvailableMatch> getJoinableMatches() {
+ // List of names of matches that are not full (then joinable)
+ List<String> joinableMatches = matches.keySet().stream()
+ .filter(name -> !matches.get(name).isFull())
+ .toList();
+ List<AvailableMatch> result = new ArrayList<>();
+
+ for (String name : matches.keySet()) {
+ Match match = matches.get(name);
+ int maxPlayers = match.getMaxPlayers();
+ int currentPlayers = match.getPlayers().size();
+
+ result.add(new AvailableMatch(name, maxPlayers, currentPlayers, match.isRejoinable()));
+ }
+
+ return result;
+ }
+
+ /**
+ * Lets the calling view join on a match with the given player username, if possible; gives back to the client
+ * an instance of its PlayerControllerRMI, to start communicating through it with the match.
+ * This method is called just by remote {@link NetworkHandlerRMI} instances.
+ *
+ * @param matchName The unique name of the match to join to
+ * @param username The chosen player username
+ * @return An instance of PlayerControllerRMI, used exclusively by the calling view
+ * @throws ChosenMatchException If the chosen match is either already full or doesn't exist
+ * @throws AlreadyUsedUsernameException If the given username is already taken
+ * @throws WrongStateException If the match is in a state during which doesn't allow players to join any more
+ * @throws WrongNameException If the name is not valid
+ * @throws RemoteException If the exportation of this object in the RMI registry failed
+ */
+ @Override
+ public PlayerControllerRMIInterface joinMatch(String matchName, String username) throws ChosenMatchException, WrongStateException, AlreadyUsedUsernameException, WrongNameException, RemoteException {
+ if (!GuiUtil.isValidName(username))
+ throw new WrongNameException("The username must be alphanumeric with maximum 32 characters");
+ if (!matches.containsKey(matchName))
+ throw new ChosenMatchException("The chosen match doesn't exist");
+ if (matches.get(matchName).isFull() && !matches.get(matchName).isRejoinable())
+ throw new ChosenMatchException("The chosen match is already full");
+
+ Match chosenMatch = matches.get(matchName);
+ PlayerControllerRMI controller = new PlayerControllerRMI(username, chosenMatch);
+
+ UnicastRemoteObject.exportObject(controller, portRMI);
+
+ return controller;
+ }
+
+ /**
+ * Create a new blank match.
+ *
+ * @param matchName The unique name to give to the new match
+ * @param maxPlayers The maximum number of player allowed on the new match
+ * @throws ChosenMatchException If the given match name is already taken
+ * @throws WrongNameException If the chosen player username doesn't meet the alphanumerical criteria
+ */
+ @Override
+ public void createMatch(String matchName, int maxPlayers) throws ChosenMatchException, WrongNameException {
+ if (!GuiUtil.isValidName(matchName)) {
+ throw new WrongNameException("The match name must be alphanumeric with maximum 32 characters");
+ }
+ synchronized (matches) {
+ if (matches.containsKey(matchName))
+ throw new ChosenMatchException("A match with the chosen name already exists");
+
+ Match newMatch = getNewMatch(maxPlayers);
+ newMatch.subscribeObserver(new MatchStatusObserver(matchName, matches));
+ matches.put(matchName, newMatch);
+ }
+ }
+
+ /**
+ * Pings the server in order to perceive if the connection is still alive and working.
+ * Always return true, since the false is implicit in returning a {@link RemoteException}
+ * when the connection is not working anymore.
+ *
+ * @return True if the connection is alive, false otherwise
+ */
+ @Override
+ public boolean ping() {
+ return true;
+ }
+
+ /**
+ * @return
+ */
+ public Map<String, Match> getJoinableMatchesMap() {
+ synchronized (matches) {
+ HashMap<String, Match> result = new HashMap<>();
+ for (String name : matches.keySet()) {
+ result.put(name, matches.get(name));
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Gets a {@link Match} from those saved in the server.
+ *
+ * @param name The unique name of the match
+ * @return The match instance
+ */
+ public Match getMatch(String name) {
+ return matches.get(name);
+ }
+
+ /**
+ * Start the RMI server.
+ *
+ * @throws RemoteException If the remote registry couldn't be exported or the communication with it failed.
+ */
+ public void startRMIServer() throws RemoteException {
+ Registry registry = LocateRegistry.createRegistry(portRMI);
+ registry.rebind("CodexNaturalisRMIServer", this);
+ }
+
+ /**
+ * Starts the TCP server.
+ */
+ public void startTCPServer() {
+ TCPServer tcpServer = new TCPServer(portTCP, this);
+ new Thread(tcpServer::listen).start();
+ }
+
+ public static void main(String[] args) throws RemoteException {
+ int portRMI;
+ int portTCP;
+
+ // If some arguments are missing, notify it to the user and exit
+ if (args.length < 2) {
+ System.err.println("Arguments missing, run the server executable with RMI port and TCP port arguments.");
+ System.err.println("Defaulting to RMI 2222 and TCP 9999 ports...");
+ portRMI = 2222;
+ portTCP = 9999;
+ } else {
+ portRMI = Integer.parseInt(args[0]);
+ portTCP = Integer.parseInt(args[1]);
+ }
+
+ Server server = new Server(portRMI, portTCP);
+
+ server.loadCrashedMatches();
+ server.startRMIServer();
+ server.startTCPServer();
+ }
+
+ /**
+ * Utility method used to restore all the matches saved in the disk after the server crashed.
+ */
+ private void loadCrashedMatches() {
+ // Look for *.match files in the current directory
+ File dir = new File(".");
+ File[] files = dir.listFiles((file, name) -> name.toLowerCase().endsWith(".match"));
+ // If any file is found
+ if (files != null) {
+ for (File file : files) {
+ try {
+ // Read each .match file from disk
+ FileInputStream fileIn = new FileInputStream(file);
+ ObjectInputStream in = new ObjectInputStream(fileIn);
+
+ // Deserialize the .match file in a Match object
+ String matchName = file.getName().replaceAll("(?i)(.*)\\.match", "$1");
+ Match match = (Match) in.readObject();
+ matches.put(matchName, match);
+ match.getPlayers().forEach((p) -> p.setConnected(false));
+ match.subscribeObserver(new MatchStatusObserver(matchName, matches));
+ in.close();
+ fileIn.close();
+ } catch (IOException | ClassNotFoundException e) {
+ System.err.println("A match couldn't be loaded from disk");
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility method to create a new blank match. It cannot be called remotely (e.g. by RMI)
+ *
+ * @param maxPlayers The maximum number of players allowed
+ * @return The new match instance
+ */
+ private static Match getNewMatch(int maxPlayers) {
+ DeckCreator creator = new DeckCreator();
+ return new Match(maxPlayers, creator.createInitialDeck(), creator.createResourceDeck(), creator.createGoldDeck(),
+ creator.createObjectiveDeck());
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/index.html b/deliveries/Test results/it.polimi.ingsw.server/index.html
new file mode 100644
index 00000000..959f1d84
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.server
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.server/index.source.html b/deliveries/Test results/it.polimi.ingsw.server/index.source.html
new file mode 100644
index 00000000..a437ed22
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.server/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.server
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.html b/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.html
new file mode 100644
index 00000000..ec347b7a
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.html
@@ -0,0 +1 @@
+AvailableMatch
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.java.html b/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.java.html
new file mode 100644
index 00000000..630b03cf
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/AvailableMatch.java.html
@@ -0,0 +1,18 @@
+AvailableMatch.java
package it.polimi.ingsw.utils;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * AvailableMatches
+ *
+ * @param name The match name
+ * @param maxPlayers The max number of players allowed in the match
+ * @param currentPlayers The number of currently joined players
+ * @param isRejoinable Whether the match has been resumed after a server crash
+ */
+public record AvailableMatch(String name, Integer maxPlayers, Integer currentPlayers, boolean isRejoinable) implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser$CardTypeAdapter.html b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser$CardTypeAdapter.html
new file mode 100644
index 00000000..4bde32f1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser$CardTypeAdapter.html
@@ -0,0 +1 @@
+CardJsonParser.CardTypeAdapter
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.html b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.html
new file mode 100644
index 00000000..dd24e4f1
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.html
@@ -0,0 +1 @@
+CardJsonParser
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.java.html b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.java.html
new file mode 100644
index 00000000..0835c6ec
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardJsonParser.java.html
@@ -0,0 +1,87 @@
+CardJsonParser.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.html b/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.html
new file mode 100644
index 00000000..f2d9c78b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.html
@@ -0,0 +1 @@
+CardsManager
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.java.html b/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.java.html
new file mode 100644
index 00000000..f3e5d31d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardsManager.java.html
@@ -0,0 +1,117 @@
+CardsManager.java
package it.polimi.ingsw.utils;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import it.polimi.ingsw.gamemodel.*;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Singleton that represents a collection of all cards actually existing in the Game, so only those used
+ * in the Match instances.
+ * It's appointed to initialise them with instances to be de-serialized from a file and make them available
+ * through getters.
+ */
+public final class CardsManager {
+ private static final CardsManager singletonInstance = new CardsManager();
+
+ private final Map<Integer, InitialCard> initialCards;
+ private final Map<Integer, GoldCard> goldCards;
+ private final Map<Integer, ResourceCard> resourceCards;
+ private final Map<Integer, Objective> objectives;
+
+ /**
+ * Private constructor since the singleton pattern is being used.
+ * Read from the JSON files, de-serialise the content in Map<Integer, XXX> objects, initialize the private attributes
+ * with these values.
+ */
+ private CardsManager() {
+ CardJsonParser parser = new CardJsonParser();
+ Gson gson = parser.getCardBuilder();
+
+ Type initialCardsType = new TypeToken<Map<Integer, InitialCard>>() { }.getType();
+ Type goldCardsType = new TypeToken<Map<Integer, GoldCard>>() { }.getType();
+ Type resourceCardsType = new TypeToken<Map<Integer, ResourceCard>>() { }.getType();
+ Type objectivesType = new TypeToken<Map<Integer, Objective>>() {}.getType();
+
+ try {
+ initialCards = gson.fromJson(getResource("/json/initial_card.json"), initialCardsType);
+ goldCards = gson.fromJson(getResource("/json/gold_card.json"), goldCardsType);
+ resourceCards = gson.fromJson(getResource("/json/resource_card.json"), resourceCardsType);
+ objectives = gson.fromJson(getResource("/json/objective_card.json"), objectivesType);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String getResource(String path) {
+ try {
+ return new String(this.getClass().getResourceAsStream(path).readAllBytes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Getter for the only possible instance available of this class, so used instead of a constructor.
+ *
+ * @return Always the same CardsManager instance
+ */
+ public static CardsManager getInstance() {
+ return singletonInstance;
+ }
+
+ /**
+ * Getter for the initial cards
+ *
+ * @return Map that matches an int ID to the corresponding initial card
+ */
+ public Map<Integer, InitialCard> getInitialCards() {
+ return initialCards;
+ }
+
+ /**
+ * Getter for the gold cards
+ *
+ * @return Map that matches an int ID to the corresponding gold card
+ */
+ public Map<Integer, GoldCard> getGoldCards() {
+ return goldCards;
+ }
+
+ /**
+ * Getter for the resource cards
+ *
+ * @return Map that matches an int ID to the corresponding resource card
+ */
+ public Map<Integer, ResourceCard> getResourceCards() {
+ return resourceCards;
+ }
+
+ /**
+ * Getter for the objectives
+ *
+ * @return Map that matches an int ID to the corresponding objective
+ */
+ public Map<Integer, Objective> getObjectives() {
+ return objectives;
+ }
+
+ /**
+ * Getter for the playable cards
+ *
+ * @return Map that matches an int ID to the corresponding playable card
+ */
+ public Map<Integer, PlayableCard> getPlayableCards() {
+ Map<Integer, PlayableCard> playableCards = new HashMap<>();
+ playableCards.putAll(goldCards);
+ playableCards.putAll(resourceCards);
+
+ return playableCards;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.html b/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.html
new file mode 100644
index 00000000..9fb049cc
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.html
@@ -0,0 +1 @@
+CardsSerializer
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.java.html b/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.java.html
new file mode 100644
index 00000000..cb262868
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/CardsSerializer.java.html
@@ -0,0 +1,916 @@
+CardsSerializer.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.html b/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.html
new file mode 100644
index 00000000..d4deb5e2
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.html
@@ -0,0 +1 @@
+DeckCreator
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.java.html b/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.java.html
new file mode 100644
index 00000000..3c9b417b
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/DeckCreator.java.html
@@ -0,0 +1,73 @@
+DeckCreator.java
package it.polimi.ingsw.utils;
+
+import it.polimi.ingsw.gamemodel.*;
+
+import java.util.Map;
+
+/**
+ * This is a temporary class, used to have all the logic related to deck creation in a single place, so that
+ * when it will be implemented correctly we know where to modify it
+ */
+public class DeckCreator {
+
+ /**
+ * Create the deck of initial cards
+ * @return a gamedeck of initial cards
+ */
+ public GameDeck<InitialCard> createInitialDeck() {
+ GameDeck<InitialCard> deck = new GameDeck<>();
+ Map<Integer, InitialCard> cards = CardsManager.getInstance().getInitialCards();
+
+ for (InitialCard card : cards.values()) {
+ deck.add(card);
+ }
+
+ return deck;
+ }
+
+ /**
+ * Create the deck of resource cards
+ * @return a gamedeck of resource cards
+ */
+ public GameDeck<ResourceCard> createResourceDeck() {
+ GameDeck<ResourceCard> deck = new GameDeck<>();
+ Map<Integer, ResourceCard> cards = CardsManager.getInstance().getResourceCards();
+
+ for (ResourceCard card : cards.values()) {
+ deck.add(card);
+ }
+
+ return deck;
+ }
+
+ /**
+ * Create the deck of gold cards
+ * @return a gamedeck of gold cards
+ */
+ public GameDeck<GoldCard> createGoldDeck() {
+ GameDeck<GoldCard> deck = new GameDeck<>();
+ Map<Integer, GoldCard> cards = CardsManager.getInstance().getGoldCards();
+
+ for (GoldCard card : cards.values()) {
+ deck.add(card);
+ }
+
+ return deck;
+ }
+
+ /**
+ * Create the deck of objective cards
+ * @return a gamedeck of objective cards
+ */
+ public GameDeck<Objective> createObjectiveDeck() {
+ GameDeck<Objective> deck = new GameDeck<>();
+ Map<Integer, Objective> cards = CardsManager.getInstance().getObjectives();
+
+ for (Objective card : cards.values()) {
+ deck.add(card);
+ }
+
+ return deck;
+ }
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.html b/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.html
new file mode 100644
index 00000000..c3499a72
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.html
@@ -0,0 +1 @@
+GuiUtil
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.java.html b/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.java.html
new file mode 100644
index 00000000..61f9fded
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/GuiUtil.java.html
@@ -0,0 +1,170 @@
+GuiUtil.java
package it.polimi.ingsw.utils;
+
+import it.polimi.ingsw.client.frontend.gui.GraphicalApplication;
+import it.polimi.ingsw.exceptions.AlreadyUsedUsernameException;
+import it.polimi.ingsw.exceptions.WrongChoiceException;
+import it.polimi.ingsw.exceptions.WrongStateException;
+import it.polimi.ingsw.exceptions.WrongTurnException;
+import it.polimi.ingsw.gamemodel.*;
+import javafx.fxml.FXMLLoader;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.util.Locale;
+
+public class GuiUtil {
+ public static String playableCardsPath = "/images/playable_cards";
+ public static String objectivesPath = "/images/objectives";
+ public static String initialsPath = "/images/initial_cards";
+ public static String pawnsPath = "/images/pawn_colors";
+
+ /**
+ * Get a node from the given FXML
+ * @param path path of the fxml
+ * @return the requested node
+ * @param <T> type of the expected node
+ * @throws IOException if there are errors reading the file
+ */
+ public static <T>T getFromFXML(String path) throws IOException {
+ FXMLLoader loader = GuiUtil.getLoader(path);
+ return loader.load();
+ }
+
+ /**
+ * Check if the username/match name is valid
+ * The name must be alphanumeric and between 1 and 32 characters
+ * @param name string to check
+ * @return if the name is valid
+ */
+ public static boolean isValidName(String name) {
+ return name.matches("^[a-zA-Z0-9]{1,32}$");
+ }
+
+ /**
+ * Get the loader from the specified path
+ * @param path file path of fxml
+ * @return loader
+ */
+ public static FXMLLoader getLoader(String path) {
+ return new FXMLLoader(GraphicalApplication.class.getResource(path));
+ }
+
+ /**
+ * Applies the specified CSS to a javafx scene parent
+ * @param w The parent to apply the css to
+ * @param path Path of the css file
+ */
+ public static void applyCSS(javafx.scene.Parent w, String path) {
+ w.getStylesheets().addAll(GraphicalApplication.class.getResource(path).toExternalForm());
+ }
+
+ /**
+ * Get the image path of a playable card
+ * @param card card to get the image
+ * @param side side of the card
+ * @return the path as a string
+ */
+ public static String getImagePath(PlayableCard card, Side side) {
+ if (side.equals(Side.FRONT)) {
+ return playableCardsPath + "/" + card.getId() + ".png";
+ } else {
+ return switch (card){
+ case GoldCard ignored -> getGoldsBack(card.getReign());
+ case ResourceCard ignored -> getResourcesBack(card.getReign());
+ };
+ }
+ }
+
+ /**
+ * Get the back of a resource card
+ * @param symbol symbol of the resource card
+ * @return the path as a string
+ */
+ public static String getResourcesBack(Symbol symbol) {
+ String reign = symbol.toString().toUpperCase();
+ return playableCardsPath + "/" + reign + "-resources-back.png";
+ }
+
+ /**
+ * Get the back of a gold card
+ * @param symbol symbol of the gold card
+ * @return the path as a string
+ */
+ public static String getGoldsBack(Symbol symbol) {
+ String reign = symbol.toString().toUpperCase();
+ return playableCardsPath + "/" + reign + "-golds-back.png";
+ }
+
+ /**
+ * Get the image path of an intial card
+ * @param card card to get the image
+ * @param side side of the card
+ * @return the path as a string
+ */
+ public static String getImagePath(InitialCard card, Side side) {
+ return initialsPath + "/" + side.toString() + "/" + card.getId() + ".png";
+ }
+
+ /**
+ * Get the image path of an objective card
+ * @param obj card to get the image
+ * @param side side of the card
+ * @return the path as a string
+ */
+ public static String getImagePath(Objective obj, Side side) {
+ if (side.equals(Side.FRONT))
+ return objectivesPath + "/" + obj.getID() + ".png";
+ else
+ return objectivesPath + "/objectives-back.png";
+ }
+
+ /**
+ * Get image path of a pawn
+ * @param color color of the pawn
+ * @return the path as a string
+ */
+ public static String getPawnImagePath(Color color) {
+ return pawnsPath + "/" + color.toString().toLowerCase(Locale.ROOT) + "-pawn.png";
+ }
+
+ /**
+ * Get image path of the black pawn
+ * @return the path as a string
+ */
+ public static String getBlackPawnImagePath() {
+ return pawnsPath + "/black-pawn.png";
+ }
+
+ /**
+ * Get the hex code of the given color
+ * @param color color to convert
+ * @return the hex code of color
+ */
+ public static String getHexFromColor(Color color) {
+ return switch (color) {
+ case RED -> "#C00402";
+ case BLUE -> "#0C6692";
+ case GREEN -> "#195C00";
+ case YELLOW -> "#BEA013";
+ };
+ }
+
+ /**
+ * Translate exception type into human-readable titles
+ * @param e exception to translate
+ * @return human-readable title
+ */
+ public static String getExceptionTitle(Exception e) {
+ return switch (e) {
+ case WrongStateException ignored -> "Wrong turn!";
+ case WrongChoiceException ignored -> "Wrong move!";
+ case AlreadyUsedUsernameException ignored -> "Username already used!";
+ case WrongTurnException ignored -> "It is not your turn!";
+ case RemoteException ignored -> "Connection error";
+ default -> "Error!";
+ };
+ }
+
+}
+
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.html b/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.html
new file mode 100644
index 00000000..8d65deb0
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.html
@@ -0,0 +1 @@
+LeaderboardEntry
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.java.html b/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.java.html
new file mode 100644
index 00000000..e03ef98d
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/LeaderboardEntry.java.html
@@ -0,0 +1,13 @@
+LeaderboardEntry.java
package it.polimi.ingsw.utils;
+
+import java.io.Serializable;
+
+/**
+ * LeaderboardEntry
+ *
+ * @param username The username of the player corresponding to this entry
+ * @param points the points obtained by the player during the game
+ * @param winner wheter the player is the winner or not (in case of draw, only the player with most objectives fullfilled wins)
+ */
+public record LeaderboardEntry(String username, Integer points, boolean winner) implements Serializable { }
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser$MessageTypeAdapter.html b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser$MessageTypeAdapter.html
new file mode 100644
index 00000000..2b7ccf72
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser$MessageTypeAdapter.html
@@ -0,0 +1 @@
+MessageJsonParser.MessageTypeAdapter
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.html b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.html
new file mode 100644
index 00000000..dc393f36
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.html
@@ -0,0 +1 @@
+MessageJsonParser
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.java.html b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.java.html
new file mode 100644
index 00000000..bda00c2c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/MessageJsonParser.java.html
@@ -0,0 +1,72 @@
+MessageJsonParser.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/Pair.html b/deliveries/Test results/it.polimi.ingsw.utils/Pair.html
new file mode 100644
index 00000000..e6dafa55
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/Pair.html
@@ -0,0 +1 @@
+Pair
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/Pair.java.html b/deliveries/Test results/it.polimi.ingsw.utils/Pair.java.html
new file mode 100644
index 00000000..763806b9
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/Pair.java.html
@@ -0,0 +1,18 @@
+Pair.java
package it.polimi.ingsw.utils;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * Represents a pair of generic values.
+ *
+ * @param first The first value of the pair
+ * @param second The second value of the pair
+ * @param <T> The type of the first value
+ * @param <U> The type of the second value
+ */
+public record Pair<T, U>(T first, U second) implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 1L;
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.html b/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.html
new file mode 100644
index 00000000..4dca9794
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.html
@@ -0,0 +1 @@
+PlacedCardRecord
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.java.html b/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.java.html
new file mode 100644
index 00000000..b01bb57f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/PlacedCardRecord.java.html
@@ -0,0 +1,15 @@
+PlacedCardRecord.java
package it.polimi.ingsw.utils;
+
+import it.polimi.ingsw.gamemodel.Side;
+
+/**
+ * PlacedCardRecord
+ *
+ * @param cardID The card ID
+ * @param x The x coordinate
+ * @param y The y coordinate
+ * @param side The chosen side
+ */
+public record PlacedCardRecord(Integer cardID, Integer x, Integer y, Side side) {
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.html b/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.html
new file mode 100644
index 00000000..8d9bfa1f
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.html
@@ -0,0 +1 @@
+RequestStatus
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.java.html b/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.java.html
new file mode 100644
index 00000000..6d6ed82c
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/RequestStatus.java.html
@@ -0,0 +1,14 @@
+RequestStatus.java
package it.polimi.ingsw.utils;
+
+import it.polimi.ingsw.controllers.PlayerController;
+import it.polimi.ingsw.server.Server;
+
+/**
+ * Represents the status of a remote request sent to a remove {@link Server} or {@link PlayerController}.
+ */
+public enum RequestStatus {
+ PENDING,
+ SUCCESSFUL,
+ FAILED;
+}
+
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.html b/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.html
new file mode 100644
index 00000000..dc7014b6
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.html
@@ -0,0 +1 @@
+TUICardParser
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.java.html b/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.java.html
new file mode 100644
index 00000000..aa8d099e
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/TUICardParser.java.html
@@ -0,0 +1,574 @@
+TUICardParser.java
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/index.html b/deliveries/Test results/it.polimi.ingsw.utils/index.html
new file mode 100644
index 00000000..b82cb080
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/index.html
@@ -0,0 +1 @@
+it.polimi.ingsw.utils
\ No newline at end of file
diff --git a/deliveries/Test results/it.polimi.ingsw.utils/index.source.html b/deliveries/Test results/it.polimi.ingsw.utils/index.source.html
new file mode 100644
index 00000000..db709598
--- /dev/null
+++ b/deliveries/Test results/it.polimi.ingsw.utils/index.source.html
@@ -0,0 +1 @@
+it.polimi.ingsw.utils
\ No newline at end of file
diff --git a/deliveries/Test results/jacoco-resources/branchfc.gif b/deliveries/Test results/jacoco-resources/branchfc.gif
new file mode 100644
index 00000000..989b46d3
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/branchfc.gif differ
diff --git a/deliveries/Test results/jacoco-resources/branchnc.gif b/deliveries/Test results/jacoco-resources/branchnc.gif
new file mode 100644
index 00000000..1933e07c
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/branchnc.gif differ
diff --git a/deliveries/Test results/jacoco-resources/branchpc.gif b/deliveries/Test results/jacoco-resources/branchpc.gif
new file mode 100644
index 00000000..cbf711b7
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/branchpc.gif differ
diff --git a/deliveries/Test results/jacoco-resources/bundle.gif b/deliveries/Test results/jacoco-resources/bundle.gif
new file mode 100644
index 00000000..fca9c53e
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/bundle.gif differ
diff --git a/deliveries/Test results/jacoco-resources/class.gif b/deliveries/Test results/jacoco-resources/class.gif
new file mode 100644
index 00000000..eb348fb0
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/class.gif differ
diff --git a/deliveries/Test results/jacoco-resources/down.gif b/deliveries/Test results/jacoco-resources/down.gif
new file mode 100644
index 00000000..440a14db
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/down.gif differ
diff --git a/deliveries/Test results/jacoco-resources/greenbar.gif b/deliveries/Test results/jacoco-resources/greenbar.gif
new file mode 100644
index 00000000..0ba65672
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/greenbar.gif differ
diff --git a/deliveries/Test results/jacoco-resources/group.gif b/deliveries/Test results/jacoco-resources/group.gif
new file mode 100644
index 00000000..a4ea580d
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/group.gif differ
diff --git a/deliveries/Test results/jacoco-resources/method.gif b/deliveries/Test results/jacoco-resources/method.gif
new file mode 100644
index 00000000..7d24707e
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/method.gif differ
diff --git a/deliveries/Test results/jacoco-resources/package.gif b/deliveries/Test results/jacoco-resources/package.gif
new file mode 100644
index 00000000..131c28da
Binary files /dev/null and b/deliveries/Test results/jacoco-resources/package.gif differ
diff --git a/deliveries/Test results/jacoco-resources/prettify.css b/deliveries/Test results/jacoco-resources/prettify.css
new file mode 100644
index 00000000..be5166e0
--- /dev/null
+++ b/deliveries/Test results/jacoco-resources/prettify.css
@@ -0,0 +1,13 @@
+/* Pretty printing styles. Used with prettify.js. */
+
+.str { color: #2A00FF; }
+.kwd { color: #7F0055; font-weight:bold; }
+.com { color: #3F5FBF; }
+.typ { color: #606; }
+.lit { color: #066; }
+.pun { color: #660; }
+.pln { color: #000; }
+.tag { color: #008; }
+.atn { color: #606; }
+.atv { color: #080; }
+.dec { color: #606; }
diff --git a/deliveries/Test results/jacoco-resources/prettify.js b/deliveries/Test results/jacoco-resources/prettify.js
new file mode 100644
index 00000000..b2766fe0
--- /dev/null
+++ b/deliveries/Test results/jacoco-resources/prettify.js
@@ -0,0 +1,1510 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ *
+ *
+ * For a fairly comprehensive set of languages see the
+ * README
+ * file that came with this source. At a minimum, the lexer should work on a
+ * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
+ * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk
+ * and a subset of Perl, but, because of commenting conventions, doesn't work on
+ * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
+ *
+ * Usage:
+ *
include this source file in an html page via
+ * {@code }
+ *
define style rules. See the example page for examples.
+ *
mark the {@code
} and {@code } tags in your source with
+ * {@code class=prettyprint.}
+ * You can also use the (html deprecated) {@code } tag, but the pretty
+ * printer needs to do more substantial DOM manipulations to support that, so
+ * some css styles may not be preserved.
+ *
+ * That's it. I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in, but if you wish, you can add
+ * another class to the {@code
} or {@code } element to specify the
+ * language, as in {@code
}. Any class that
+ * starts with "lang-" followed by a file extension, specifies the file type.
+ * See the "lang-*.js" files in this directory for code that implements
+ * per-language file handlers.
+ *
+ * Change log:
+ * cbeust, 2006/08/22
+ *
+ * Java annotations (start with "@") are now captured as literals ("lit")
+ *
+ * @requires console
+ */
+
+// JSLint declarations
+/*global console, document, navigator, setTimeout, window */
+
+/**
+ * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
+ * UI events.
+ * If set to {@code false}, {@code prettyPrint()} is synchronous.
+ */
+window['PR_SHOULD_USE_CONTINUATION'] = true;
+
+/** the number of characters between tab columns */
+window['PR_TAB_WIDTH'] = 8;
+
+/** Walks the DOM returning a properly escaped version of innerHTML.
+ * @param {Node} node
+ * @param {Array.} out output buffer that receives chunks of HTML.
+ */
+window['PR_normalizedHtml']
+
+/** Contains functions for creating and registering new language handlers.
+ * @type {Object}
+ */
+ = window['PR']
+
+/** Pretty print a chunk of code.
+ *
+ * @param {string} sourceCodeHtml code as html
+ * @return {string} code as html, but prettier
+ */
+ = window['prettyPrintOne']
+/** Find all the {@code
} and {@code } tags in the DOM with
+ * {@code class=prettyprint} and prettify them.
+ * @param {Function?} opt_whenDone if specified, called when the last entry
+ * has been finished.
+ */
+ = window['prettyPrint'] = void 0;
+
+/** browser detection. @extern @returns false if not IE, otherwise the major version. */
+window['_pr_isIE6'] = function () {
+ var ieVersion = navigator && navigator.userAgent &&
+ navigator.userAgent.match(/\bMSIE ([678])\./);
+ ieVersion = ieVersion ? +ieVersion[1] : false;
+ window['_pr_isIE6'] = function () { return ieVersion; };
+ return ieVersion;
+};
+
+
+(function () {
+ // Keyword lists for various languages.
+ var FLOW_CONTROL_KEYWORDS =
+ "break continue do else for if return while ";
+ var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
+ "double enum extern float goto int long register short signed sizeof " +
+ "static struct switch typedef union unsigned void volatile ";
+ var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
+ "new operator private protected public this throw true try typeof ";
+ var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
+ "concept concept_map const_cast constexpr decltype " +
+ "dynamic_cast explicit export friend inline late_check " +
+ "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
+ "template typeid typename using virtual wchar_t where ";
+ var JAVA_KEYWORDS = COMMON_KEYWORDS +
+ "abstract boolean byte extends final finally implements import " +
+ "instanceof null native package strictfp super synchronized throws " +
+ "transient ";
+ var CSHARP_KEYWORDS = JAVA_KEYWORDS +
+ "as base by checked decimal delegate descending event " +
+ "fixed foreach from group implicit in interface internal into is lock " +
+ "object out override orderby params partial readonly ref sbyte sealed " +
+ "stackalloc string select uint ulong unchecked unsafe ushort var ";
+ var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
+ "debugger eval export function get null set undefined var with " +
+ "Infinity NaN ";
+ var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
+ "goto if import last local my next no our print package redo require " +
+ "sub undef unless until use wantarray while BEGIN END ";
+ var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
+ "elif except exec finally from global import in is lambda " +
+ "nonlocal not or pass print raise try with yield " +
+ "False True None ";
+ var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
+ " defined elsif end ensure false in module next nil not or redo rescue " +
+ "retry self super then true undef unless until when yield BEGIN END ";
+ var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
+ "function in local set then until ";
+ var ALL_KEYWORDS = (
+ CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
+ PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
+
+ // token style names. correspond to css classes
+ /** token style for a string literal */
+ var PR_STRING = 'str';
+ /** token style for a keyword */
+ var PR_KEYWORD = 'kwd';
+ /** token style for a comment */
+ var PR_COMMENT = 'com';
+ /** token style for a type */
+ var PR_TYPE = 'typ';
+ /** token style for a literal value. e.g. 1, null, true. */
+ var PR_LITERAL = 'lit';
+ /** token style for a punctuation string. */
+ var PR_PUNCTUATION = 'pun';
+ /** token style for a punctuation string. */
+ var PR_PLAIN = 'pln';
+
+ /** token style for an sgml tag. */
+ var PR_TAG = 'tag';
+ /** token style for a markup declaration such as a DOCTYPE. */
+ var PR_DECLARATION = 'dec';
+ /** token style for embedded source. */
+ var PR_SOURCE = 'src';
+ /** token style for an sgml attribute name. */
+ var PR_ATTRIB_NAME = 'atn';
+ /** token style for an sgml attribute value. */
+ var PR_ATTRIB_VALUE = 'atv';
+
+ /**
+ * A class that indicates a section of markup that is not code, e.g. to allow
+ * embedding of line numbers within code listings.
+ */
+ var PR_NOCODE = 'nocode';
+
+ /** A set of tokens that can precede a regular expression literal in
+ * javascript.
+ * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
+ * list, but I've removed ones that might be problematic when seen in
+ * languages that don't support regular expression literals.
+ *
+ *
Specifically, I've removed any keywords that can't precede a regexp
+ * literal in a syntactically legal javascript program, and I've removed the
+ * "in" keyword since it's not a keyword in many languages, and might be used
+ * as a count of inches.
+ *
+ *
The link a above does not accurately describe EcmaScript rules since
+ * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
+ * very well in practice.
+ *
+ * @private
+ */
+ var REGEXP_PRECEDER_PATTERN = function () {
+ var preceders = [
+ "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
+ "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
+ "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
+ "<", "<<", "<<=", "<=", "=", "==", "===", ">",
+ ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
+ "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
+ "||=", "~" /* handles =~ and !~ */,
+ "break", "case", "continue", "delete",
+ "do", "else", "finally", "instanceof",
+ "return", "throw", "try", "typeof"
+ ];
+ var pattern = '(?:^^|[+-]';
+ for (var i = 0; i < preceders.length; ++i) {
+ pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
+ }
+ pattern += ')\\s*'; // matches at end, and matches empty string
+ return pattern;
+ // CAVEAT: this does not properly handle the case where a regular
+ // expression immediately follows another since a regular expression may
+ // have flags for case-sensitivity and the like. Having regexp tokens
+ // adjacent is not valid in any language I'm aware of, so I'm punting.
+ // TODO: maybe style special characters inside a regexp as punctuation.
+ }();
+
+ // Define regexps here so that the interpreter doesn't have to create an
+ // object each time the function containing them is called.
+ // The language spec requires a new object created even if you don't access
+ // the $1 members.
+ var pr_amp = /&/g;
+ var pr_lt = //g;
+ var pr_quot = /\"/g;
+ /** like textToHtml but escapes double quotes to be attribute safe. */
+ function attribToHtml(str) {
+ return str.replace(pr_amp, '&')
+ .replace(pr_lt, '<')
+ .replace(pr_gt, '>')
+ .replace(pr_quot, '"');
+ }
+
+ /** escapest html special characters to html. */
+ function textToHtml(str) {
+ return str.replace(pr_amp, '&')
+ .replace(pr_lt, '<')
+ .replace(pr_gt, '>');
+ }
+
+
+ var pr_ltEnt = /</g;
+ var pr_gtEnt = />/g;
+ var pr_aposEnt = /'/g;
+ var pr_quotEnt = /"/g;
+ var pr_ampEnt = /&/g;
+ var pr_nbspEnt = / /g;
+ /** unescapes html to plain text. */
+ function htmlToText(html) {
+ var pos = html.indexOf('&');
+ if (pos < 0) { return html; }
+ // Handle numeric entities specially. We can't use functional substitution
+ // since that doesn't work in older versions of Safari.
+ // These should be rare since most browsers convert them to normal chars.
+ for (--pos; (pos = html.indexOf('', pos + 1)) >= 0;) {
+ var end = html.indexOf(';', pos);
+ if (end >= 0) {
+ var num = html.substring(pos + 3, end);
+ var radix = 10;
+ if (num && num.charAt(0) === 'x') {
+ num = num.substring(1);
+ radix = 16;
+ }
+ var codePoint = parseInt(num, radix);
+ if (!isNaN(codePoint)) {
+ html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
+ html.substring(end + 1));
+ }
+ }
+ }
+
+ return html.replace(pr_ltEnt, '<')
+ .replace(pr_gtEnt, '>')
+ .replace(pr_aposEnt, "'")
+ .replace(pr_quotEnt, '"')
+ .replace(pr_nbspEnt, ' ')
+ .replace(pr_ampEnt, '&');
+ }
+
+ /** is the given node's innerHTML normally unescaped? */
+ function isRawContent(node) {
+ return 'XMP' === node.tagName;
+ }
+
+ var newlineRe = /[\r\n]/g;
+ /**
+ * Are newlines and adjacent spaces significant in the given node's innerHTML?
+ */
+ function isPreformatted(node, content) {
+ // PRE means preformatted, and is a very common case, so don't create
+ // unnecessary computed style objects.
+ if ('PRE' === node.tagName) { return true; }
+ if (!newlineRe.test(content)) { return true; } // Don't care
+ var whitespace = '';
+ // For disconnected nodes, IE has no currentStyle.
+ if (node.currentStyle) {
+ whitespace = node.currentStyle.whiteSpace;
+ } else if (window.getComputedStyle) {
+ // Firefox makes a best guess if node is disconnected whereas Safari
+ // returns the empty string.
+ whitespace = window.getComputedStyle(node, null).whiteSpace;
+ }
+ return !whitespace || whitespace === 'pre';
+ }
+
+ function normalizedHtml(node, out, opt_sortAttrs) {
+ switch (node.nodeType) {
+ case 1: // an element
+ var name = node.tagName.toLowerCase();
+
+ out.push('<', name);
+ var attrs = node.attributes;
+ var n = attrs.length;
+ if (n) {
+ if (opt_sortAttrs) {
+ var sortedAttrs = [];
+ for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; }
+ sortedAttrs.sort(function (a, b) {
+ return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1;
+ });
+ attrs = sortedAttrs;
+ }
+ for (var i = 0; i < n; ++i) {
+ var attr = attrs[i];
+ if (!attr.specified) { continue; }
+ out.push(' ', attr.name.toLowerCase(),
+ '="', attribToHtml(attr.value), '"');
+ }
+ }
+ out.push('>');
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ normalizedHtml(child, out, opt_sortAttrs);
+ }
+ if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
+ out.push('<\/', name, '>');
+ }
+ break;
+ case 3: case 4: // text
+ out.push(textToHtml(node.nodeValue));
+ break;
+ }
+ }
+
+ /**
+ * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
+ * matches the union o the sets o strings matched d by the input RegExp.
+ * Since it matches globally, if the input strings have a start-of-input
+ * anchor (/^.../), it is ignored for the purposes of unioning.
+ * @param {Array.} regexs non multiline, non-global regexs.
+ * @return {RegExp} a global regex.
+ */
+ function combinePrefixPatterns(regexs) {
+ var capturedGroupIndex = 0;
+
+ var needToFoldCase = false;
+ var ignoreCase = false;
+ for (var i = 0, n = regexs.length; i < n; ++i) {
+ var regex = regexs[i];
+ if (regex.ignoreCase) {
+ ignoreCase = true;
+ } else if (/[a-z]/i.test(regex.source.replace(
+ /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
+ needToFoldCase = true;
+ ignoreCase = false;
+ break;
+ }
+ }
+
+ function decodeEscape(charsetPart) {
+ if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
+ switch (charsetPart.charAt(1)) {
+ case 'b': return 8;
+ case 't': return 9;
+ case 'n': return 0xa;
+ case 'v': return 0xb;
+ case 'f': return 0xc;
+ case 'r': return 0xd;
+ case 'u': case 'x':
+ return parseInt(charsetPart.substring(2), 16)
+ || charsetPart.charCodeAt(1);
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ return parseInt(charsetPart.substring(1), 8);
+ default: return charsetPart.charCodeAt(1);
+ }
+ }
+
+ function encodeEscape(charCode) {
+ if (charCode < 0x20) {
+ return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
+ }
+ var ch = String.fromCharCode(charCode);
+ if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
+ ch = '\\' + ch;
+ }
+ return ch;
+ }
+
+ function caseFoldCharset(charSet) {
+ var charsetParts = charSet.substring(1, charSet.length - 1).match(
+ new RegExp(
+ '\\\\u[0-9A-Fa-f]{4}'
+ + '|\\\\x[0-9A-Fa-f]{2}'
+ + '|\\\\[0-3][0-7]{0,2}'
+ + '|\\\\[0-7]{1,2}'
+ + '|\\\\[\\s\\S]'
+ + '|-'
+ + '|[^-\\\\]',
+ 'g'));
+ var groups = [];
+ var ranges = [];
+ var inverse = charsetParts[0] === '^';
+ for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
+ var p = charsetParts[i];
+ switch (p) {
+ case '\\B': case '\\b':
+ case '\\D': case '\\d':
+ case '\\S': case '\\s':
+ case '\\W': case '\\w':
+ groups.push(p);
+ continue;
+ }
+ var start = decodeEscape(p);
+ var end;
+ if (i + 2 < n && '-' === charsetParts[i + 1]) {
+ end = decodeEscape(charsetParts[i + 2]);
+ i += 2;
+ } else {
+ end = start;
+ }
+ ranges.push([start, end]);
+ // If the range might intersect letters, then expand it.
+ if (!(end < 65 || start > 122)) {
+ if (!(end < 65 || start > 90)) {
+ ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
+ }
+ if (!(end < 97 || start > 122)) {
+ ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
+ }
+ }
+ }
+
+ // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
+ // -> [[1, 12], [14, 14], [16, 17]]
+ ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
+ var consolidatedRanges = [];
+ var lastRange = [NaN, NaN];
+ for (var i = 0; i < ranges.length; ++i) {
+ var range = ranges[i];
+ if (range[0] <= lastRange[1] + 1) {
+ lastRange[1] = Math.max(lastRange[1], range[1]);
+ } else {
+ consolidatedRanges.push(lastRange = range);
+ }
+ }
+
+ var out = ['['];
+ if (inverse) { out.push('^'); }
+ out.push.apply(out, groups);
+ for (var i = 0; i < consolidatedRanges.length; ++i) {
+ var range = consolidatedRanges[i];
+ out.push(encodeEscape(range[0]));
+ if (range[1] > range[0]) {
+ if (range[1] + 1 > range[0]) { out.push('-'); }
+ out.push(encodeEscape(range[1]));
+ }
+ }
+ out.push(']');
+ return out.join('');
+ }
+
+ function allowAnywhereFoldCaseAndRenumberGroups(regex) {
+ // Split into character sets, escape sequences, punctuation strings
+ // like ('(', '(?:', ')', '^'), and runs of characters that do not
+ // include any of the above.
+ var parts = regex.source.match(
+ new RegExp(
+ '(?:'
+ + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
+ + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
+ + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
+ + '|\\\\[0-9]+' // a back-reference or octal escape
+ + '|\\\\[^ux0-9]' // other escape sequence
+ + '|\\(\\?[:!=]' // start of a non-capturing group
+ + '|[\\(\\)\\^]' // start/emd of a group, or line start
+ + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
+ + ')',
+ 'g'));
+ var n = parts.length;
+
+ // Maps captured group numbers to the number they will occupy in
+ // the output or to -1 if that has not been determined, or to
+ // undefined if they need not be capturing in the output.
+ var capturedGroups = [];
+
+ // Walk over and identify back references to build the capturedGroups
+ // mapping.
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
+ var p = parts[i];
+ if (p === '(') {
+ // groups are 1-indexed, so max group index is count of '('
+ ++groupIndex;
+ } else if ('\\' === p.charAt(0)) {
+ var decimalValue = +p.substring(1);
+ if (decimalValue && decimalValue <= groupIndex) {
+ capturedGroups[decimalValue] = -1;
+ }
+ }
+ }
+
+ // Renumber groups and reduce capturing groups to non-capturing groups
+ // where possible.
+ for (var i = 1; i < capturedGroups.length; ++i) {
+ if (-1 === capturedGroups[i]) {
+ capturedGroups[i] = ++capturedGroupIndex;
+ }
+ }
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
+ var p = parts[i];
+ if (p === '(') {
+ ++groupIndex;
+ if (capturedGroups[groupIndex] === undefined) {
+ parts[i] = '(?:';
+ }
+ } else if ('\\' === p.charAt(0)) {
+ var decimalValue = +p.substring(1);
+ if (decimalValue && decimalValue <= groupIndex) {
+ parts[i] = '\\' + capturedGroups[groupIndex];
+ }
+ }
+ }
+
+ // Remove any prefix anchors so that the output will match anywhere.
+ // ^^ really does mean an anchored match though.
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
+ if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
+ }
+
+ // Expand letters to groupts to handle mixing of case-sensitive and
+ // case-insensitive patterns if necessary.
+ if (regex.ignoreCase && needToFoldCase) {
+ for (var i = 0; i < n; ++i) {
+ var p = parts[i];
+ var ch0 = p.charAt(0);
+ if (p.length >= 2 && ch0 === '[') {
+ parts[i] = caseFoldCharset(p);
+ } else if (ch0 !== '\\') {
+ // TODO: handle letters in numeric escapes.
+ parts[i] = p.replace(
+ /[a-zA-Z]/g,
+ function (ch) {
+ var cc = ch.charCodeAt(0);
+ return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
+ });
+ }
+ }
+ }
+
+ return parts.join('');
+ }
+
+ var rewritten = [];
+ for (var i = 0, n = regexs.length; i < n; ++i) {
+ var regex = regexs[i];
+ if (regex.global || regex.multiline) { throw new Error('' + regex); }
+ rewritten.push(
+ '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
+ }
+
+ return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
+ }
+
+ var PR_innerHtmlWorks = null;
+ function getInnerHtml(node) {
+ // inner html is hopelessly broken in Safari 2.0.4 when the content is
+ // an html description of well formed XML and the containing tag is a PRE
+ // tag, so we detect that case and emulate innerHTML.
+ if (null === PR_innerHtmlWorks) {
+ var testNode = document.createElement('PRE');
+ testNode.appendChild(
+ document.createTextNode('\n'));
+ PR_innerHtmlWorks = !/)[\r\n]+/g, '$1')
+ .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
+ }
+ return content;
+ }
+
+ var out = [];
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ normalizedHtml(child, out);
+ }
+ return out.join('');
+ }
+
+ /** returns a function that expand tabs to spaces. This function can be fed
+ * successive chunks of text, and will maintain its own internal state to
+ * keep track of how tabs are expanded.
+ * @return {function (string) : string} a function that takes
+ * plain text and return the text with tabs expanded.
+ * @private
+ */
+ function makeTabExpander(tabWidth) {
+ var SPACES = ' ';
+ var charInLine = 0;
+
+ return function (plainText) {
+ // walk over each character looking for tabs and newlines.
+ // On tabs, expand them. On newlines, reset charInLine.
+ // Otherwise increment charInLine
+ var out = null;
+ var pos = 0;
+ for (var i = 0, n = plainText.length; i < n; ++i) {
+ var ch = plainText.charAt(i);
+
+ switch (ch) {
+ case '\t':
+ if (!out) { out = []; }
+ out.push(plainText.substring(pos, i));
+ // calculate how much space we need in front of this part
+ // nSpaces is the amount of padding -- the number of spaces needed
+ // to move us to the next column, where columns occur at factors of
+ // tabWidth.
+ var nSpaces = tabWidth - (charInLine % tabWidth);
+ charInLine += nSpaces;
+ for (; nSpaces >= 0; nSpaces -= SPACES.length) {
+ out.push(SPACES.substring(0, nSpaces));
+ }
+ pos = i + 1;
+ break;
+ case '\n':
+ charInLine = 0;
+ break;
+ default:
+ ++charInLine;
+ }
+ }
+ if (!out) { return plainText; }
+ out.push(plainText.substring(pos));
+ return out.join('');
+ };
+ }
+
+ var pr_chunkPattern = new RegExp(
+ '[^<]+' // A run of characters other than '<'
+ + '|<\!--[\\s\\S]*?--\>' // an HTML comment
+ + '|' // a CDATA section
+ // a probable tag that should not be highlighted
+ + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
+ + '|<', // A '<' that does not begin a larger chunk
+ 'g');
+ var pr_commentPrefix = /^<\!--/;
+ var pr_cdataPrefix = /^) into their textual equivalent.
+ *
+ * @param {string} s html where whitespace is considered significant.
+ * @return {Object} source code and extracted tags.
+ * @private
+ */
+ function extractTags(s) {
+ // since the pattern has the 'g' modifier and defines no capturing groups,
+ // this will return a list of all chunks which we then classify and wrap as
+ // PR_Tokens
+ var matches = s.match(pr_chunkPattern);
+ var sourceBuf = [];
+ var sourceBufLen = 0;
+ var extractedTags = [];
+ if (matches) {
+ for (var i = 0, n = matches.length; i < n; ++i) {
+ var match = matches[i];
+ if (match.length > 1 && match.charAt(0) === '<') {
+ if (pr_commentPrefix.test(match)) { continue; }
+ if (pr_cdataPrefix.test(match)) {
+ // strip CDATA prefix and suffix. Don't unescape since it's CDATA
+ sourceBuf.push(match.substring(9, match.length - 3));
+ sourceBufLen += match.length - 12;
+ } else if (pr_brPrefix.test(match)) {
+ // tags are lexically significant so convert them to text.
+ // This is undone later.
+ sourceBuf.push('\n');
+ ++sourceBufLen;
+ } else {
+ if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
+ // A will start a section that should be
+ // ignored. Continue walking the list until we see a matching end
+ // tag.
+ var name = match.match(pr_tagNameRe)[2];
+ var depth = 1;
+ var j;
+ end_tag_loop:
+ for (j = i + 1; j < n; ++j) {
+ var name2 = matches[j].match(pr_tagNameRe);
+ if (name2 && name2[2] === name) {
+ if (name2[1] === '/') {
+ if (--depth === 0) { break end_tag_loop; }
+ } else {
+ ++depth;
+ }
+ }
+ }
+ if (j < n) {
+ extractedTags.push(
+ sourceBufLen, matches.slice(i, j + 1).join(''));
+ i = j;
+ } else { // Ignore unclosed sections.
+ extractedTags.push(sourceBufLen, match);
+ }
+ } else {
+ extractedTags.push(sourceBufLen, match);
+ }
+ }
+ } else {
+ var literalText = htmlToText(match);
+ sourceBuf.push(literalText);
+ sourceBufLen += literalText.length;
+ }
+ }
+ }
+ return { source: sourceBuf.join(''), tags: extractedTags };
+ }
+
+ /** True if the given tag contains a class attribute with the nocode class. */
+ function isNoCodeTag(tag) {
+ return !!tag
+ // First canonicalize the representation of attributes
+ .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
+ ' $1="$2$3$4"')
+ // Then look for the attribute we want.
+ .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
+ }
+
+ /**
+ * Apply the given language handler to sourceCode and add the resulting
+ * decorations to out.
+ * @param {number} basePos the index of sourceCode within the chunk of source
+ * whose decorations are already present on out.
+ */
+ function appendDecorations(basePos, sourceCode, langHandler, out) {
+ if (!sourceCode) { return; }
+ var job = {
+ source: sourceCode,
+ basePos: basePos
+ };
+ langHandler(job);
+ out.push.apply(out, job.decorations);
+ }
+
+ /** Given triples of [style, pattern, context] returns a lexing function,
+ * The lexing function interprets the patterns to find token boundaries and
+ * returns a decoration list of the form
+ * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+ * where index_n is an index into the sourceCode, and style_n is a style
+ * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
+ * all characters in sourceCode[index_n-1:index_n].
+ *
+ * The stylePatterns is a list whose elements have the form
+ * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
+ *
+ * Style is a style constant like PR_PLAIN, or can be a string of the
+ * form 'lang-FOO', where FOO is a language extension describing the
+ * language of the portion of the token in $1 after pattern executes.
+ * E.g., if style is 'lang-lisp', and group 1 contains the text
+ * '(hello (world))', then that portion of the token will be passed to the
+ * registered lisp handler for formatting.
+ * The text before and after group 1 will be restyled using this decorator
+ * so decorators should take care that this doesn't result in infinite
+ * recursion. For example, the HTML lexer rule for SCRIPT elements looks
+ * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match
+ * '