diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java index 5760136..a367275 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/Client.java @@ -341,11 +341,11 @@ public class Client { * @param parameter a string according to {@link GuiParameters} and {@link ClientGameInfoHandler} * can be empty * @param data some information in a string, separators can be $ or : - * TODO(Seraina&Sebi): evtl. auslagern? + * TODO(Seraina&Sebi): evtl. auslagern? */ public void sendToGUI(String parameter, String data) { try { - if(!parameter.equals(GuiParameters.updateGameState)) { + if (!parameter.equals(GuiParameters.updateGameState)) { LOGGER.debug("GUI: PARAMETER:" + parameter + ", DATA: " + data); } switch (parameter) { @@ -366,7 +366,7 @@ public class Client { int position = Integer.parseInt(data); determineNoiseDisplay(position); } catch (Exception e) { - LOGGER.warn("Not a position given for noise " +e.getMessage()); + LOGGER.warn("Not a position given for noise " + e.getMessage()); } break; case GuiParameters.listOfLobbies: @@ -406,6 +406,7 @@ public class Client { } } catch (Exception e) { LOGGER.warn("Communication with GUI currently not possible: " + e.getMessage()); + LOGGER.debug(e.getCause() + " " + e.getStackTrace().toString()); } @@ -443,9 +444,8 @@ public class Client { ObservableList list = new SimpleListProperty<>(); int n = arr.length; for (int i = 0; i < n; i = i + 1) { - list.add(new SimpleStringProperty(arr[i])); + loungeSceneViewController.addClientToList(arr[i]); } - loungeSceneViewController.updateClientListView(list); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java index 41dbad6..6ede8e5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/JClientProtocolParser.java @@ -65,17 +65,21 @@ public class JClientProtocolParser { c.changeUsername(msg.substring(6)); break; case Protocol.printToGUI: + LOGGER.info("First line of printToGui case!"); String substring = msg.substring(6); + LOGGER.debug("Following parameters where recieved: " + substring); int index = substring.indexOf("$"); + LOGGER.debug("Index of $: " + index); String parameter = ""; String data = substring; try { - parameter = substring.substring(0,index); - data = substring.substring(index+1); + parameter = substring.substring(0, index); + data = substring.substring(index + 1); + LOGGER.debug("Parameter: " + parameter + ". Data: " + data); } catch (Exception e) { LOGGER.warn("No parameter in PTGUI"); } - c.sendToGUI(parameter,data); + c.sendToGUI(parameter, data); break; case Protocol.positionOfClient: try { @@ -85,7 +89,7 @@ public class JClientProtocolParser { LOGGER.warn(msg.substring(6)); } break; - default: + default: System.out.println("Received unknown command: " + msg); } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/ClientListItem.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/ClientListItem.java index b0f17c5..8c815ea 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/ClientListItem.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/ClientListItem.java @@ -6,13 +6,18 @@ import javafx.beans.property.SimpleStringProperty; public class ClientListItem { private SimpleStringProperty name; - private final SimpleIntegerProperty id; + private final int id; - public ClientListItem(SimpleStringProperty name, SimpleIntegerProperty id) { - this.name = name; + private static int uid = 0; + public ClientListItem(String name, int id) { + this.name = new SimpleStringProperty(name); this.id = id; } + public ClientListItem(String name) { + this(name, uid++); + } + @Override public String toString(){ return name + " ID: " + id; @@ -31,10 +36,10 @@ public class ClientListItem { } public int getId() { - return id.get(); + return id; } - public SimpleIntegerProperty idProperty() { + public int clientID() { return id; } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LobbyListItem.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LobbyListItem.java index b3286e1..e3f4fbc 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LobbyListItem.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LobbyListItem.java @@ -4,6 +4,7 @@ import java.util.Set; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; @@ -13,61 +14,100 @@ import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ToggleButton; -public class LobbyListItem implements Observable { +public class LobbyListItem { private final SimpleStringProperty lobbyID; private final SimpleStringProperty adminName; private ObservableList clientsInLobby; - private final Button button; + private SimpleBooleanProperty ownedByClient; private SimpleBooleanProperty isOpen; - public LobbyListItem(SimpleStringProperty lobbyID, SimpleStringProperty adminName, - ObservableList clientsInLobby, Button button, - SimpleBooleanProperty ownedByClient) { + private final int MAX_CAPACITY = 6; + private SimpleIntegerProperty noOfPlayersInLobby; + + public LobbyListItem(SimpleStringProperty lobbyID, + SimpleStringProperty adminName, + SimpleBooleanProperty ownedByClient, SimpleBooleanProperty isOpen, + SimpleIntegerProperty noOfPlayersInLobby) { this.lobbyID = lobbyID; this.adminName = adminName; this.clientsInLobby = clientsInLobby; - this.button = button; + this.ownedByClient = ownedByClient; + this.isOpen = isOpen; + this.noOfPlayersInLobby = noOfPlayersInLobby; } - /** - * Adds an {@link InvalidationListener} which will be notified whenever the {@code Observable} - * becomes invalid. If the same listener is added more than once, then it will be notified more - * than once. That is, no check is made to ensure uniqueness. - *

- * Note that the same actual {@code InvalidationListener} instance may be safely registered for - * different {@code Observables}. - *

- * The {@code Observable} stores a strong reference to the listener which will prevent the - * listener from being garbage collected and may result in a memory leak. It is recommended to - * either unregister a listener by calling {@link #removeListener(InvalidationListener) - * removeListener} after use or to use an instance of {@link WeakInvalidationListener} avoid this - * situation. - * - * @param listener The listener to register - * @throws NullPointerException if the listener is null - * @see #removeListener(InvalidationListener) - */ - @Override - public void addListener(InvalidationListener listener) { - + public String getLobbyID() { + return lobbyID.get(); } - /** - * Removes the given listener from the list of listeners, that are notified whenever the value of - * the {@code Observable} becomes invalid. - *

- * If the given listener has not been previously registered (i.e. it was never added) then this - * method call is a no-op. If it had been previously added then it will be removed. If it had been - * added more than once, then only the first occurrence will be removed. - * - * @param listener The listener to remove - * @throws NullPointerException if the listener is null - * @see #addListener(InvalidationListener) - */ - @Override - public void removeListener(InvalidationListener listener) { + public SimpleStringProperty lobbyIDProperty() { + return lobbyID; + } + public void setLobbyID(String lobbyID) { + this.lobbyID.set(lobbyID); + } + + public String getAdminName() { + return adminName.get(); + } + + public SimpleStringProperty adminNameProperty() { + return adminName; + } + + public void setAdminName(String adminName) { + this.adminName.set(adminName); + } + + public ObservableList getClientsInLobby() { + return clientsInLobby; + } + + public void setClientsInLobby( + ObservableList clientsInLobby) { + this.clientsInLobby = clientsInLobby; + } + + public boolean isOwnedByClient() { + return ownedByClient.get(); + } + + public SimpleBooleanProperty ownedByClientProperty() { + return ownedByClient; + } + + public void setOwnedByClient(boolean ownedByClient) { + this.ownedByClient.set(ownedByClient); + } + + public boolean isIsOpen() { + return isOpen.get(); + } + + public SimpleBooleanProperty isOpenProperty() { + return isOpen; + } + + public void setIsOpen(boolean isOpen) { + this.isOpen.set(isOpen); + } + + public int getMAX_CAPACITY() { + return MAX_CAPACITY; + } + + public int getNoOfPlayersInLobby() { + return noOfPlayersInLobby.get(); + } + + public SimpleIntegerProperty noOfPlayersInLobbyProperty() { + return noOfPlayersInLobby; + } + + public void setNoOfPlayersInLobby(int noOfPlayersInLobby) { + this.noOfPlayersInLobby.set(noOfPlayersInLobby); } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LoungeSceneViewController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LoungeSceneViewController.java index 1334dda..809b6c8 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LoungeSceneViewController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/client/gui/lounge/LoungeSceneViewController.java @@ -3,20 +3,16 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.lounge; import ch.unibas.dmi.dbis.cs108.BudaLogConfig; import ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.ClientModel; import ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.ChatApp; -import ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.events.ChangeNameButtonPressedEventHandler; -import ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.events.LeaveServerButtonPressedEventHandler; import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol; +import ch.unibas.dmi.dbis.cs108.multiplayer.server.JServerProtocolParser; import java.net.URL; import java.util.HashMap; import java.util.Iterator; -import java.util.List; -import java.util.Objects; import java.util.ResourceBundle; import javafx.application.Application; import javafx.application.Platform; -import javafx.beans.binding.StringBinding; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleMapProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; @@ -28,7 +24,7 @@ import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; -import javafx.scene.Node; +import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ListCell; @@ -40,6 +36,7 @@ import javafx.scene.control.ToolBar; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; import javafx.scene.text.Text; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -65,9 +62,9 @@ public class LoungeSceneViewController implements Initializable { @FXML private AnchorPane gameAnchorPane; @FXML - private ListView LobbyListView; + private ListView LobbyListView; @FXML - private ListView ClientListView; + private ListView ClientListView; @FXML private Button ChangeNameButton; @FXML @@ -83,6 +80,9 @@ public class LoungeSceneViewController implements Initializable { private static ChatApp chatApp; private ChatApp cApp; + ObservableList clients = FXCollections.observableArrayList(); + ObservableList lobbies = FXCollections.observableArrayList(); + private ObservableMap> lobbyToMemberssMap; private HashMap clientToLobbyMap; @@ -117,6 +117,171 @@ public class LoungeSceneViewController implements Initializable { ClientListView.setVisible(true); ClientListView.setItems(client.getAllClients()); addChatView(); + + ClientListView.setItems(clients); + ClientListView.setCellFactory(param -> { + ListCell cell = new ListCell<>() { + Label name = new Label(); + Label id = new Label(); + HBox nameAndId = new HBox(name, id); + + { + nameAndId.setAlignment(Pos.CENTER_LEFT); + } + + /** + * The updateItem method should not be called by developers, but it is the + * best method for developers to override to allow for them to customise the + * visuals of the cell. To clarify, developers should never call this method + * in their code (they should leave it up to the UI control, such as the + * {@link ListView} control) to call this method. However, the purpose of + * having the updateItem method is so that developers, when specifying + * custom cell factories (again, like the ListView {@link + * ListView#cellFactoryProperty() cell factory}), the updateItem method can + * be overridden to allow for complete customisation of the cell. + * + *

It is very important that subclasses + * of Cell override the updateItem method properly, as failure to do so will + * lead to issues such as blank cells or cells with unexpected content + * appearing within them. Here is an example of how to properly override the + * updateItem method: + * + *

+         * protected void updateItem(T item, boolean empty) {
+         *     super.updateItem(item, empty);
+         *
+         *     if (empty || item == null) {
+         *         setText(null);
+         *         setGraphic(null);
+         *     } else {
+         *         setText(item.toString());
+         *     }
+         * }
+         * 
+ * + *

Note in this code sample two important points: + *

    + *
  1. We call the super.updateItem(T, boolean) method. If this is not + * done, the item and empty properties are not correctly set, and you are + * likely to end up with graphical issues.
  2. + *
  3. We test for the empty condition, and if true, we + * set the text and graphic properties to null. If we do not do this, + * it is almost guaranteed that end users will see graphical artifacts + * in cells unexpectedly.
  4. + *
+ * @param item The new item for the cell. + * + * @param empty whether or not this cell represents data from the list. If + * it is empty, then it does not represent any domain data, but + * is a cell + */ + @Override + protected void updateItem(ClientListItem item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setText(null); + setGraphic(null); + } else { + LOGGER.debug("In updateItem(item, empty) Method. Else branch -> nonnull item"); + name.setText(item.getName()); + id.setText(String.valueOf(item.getId())); + setGraphic(nameAndId); + } + } + }; + return cell; + }); + + LobbyListView.setItems(lobbies); + LobbyListView.setCellFactory(param -> { + ListCell cell = new ListCell<>() { + + Label lobbyID = new Label(); + Label adminName = new Label(); + Label lobbyIsOpen = new Label(); + Label noOfPlayersInLobby = new Label(); + Button startOrJoin = new Button(); + HBox head = new HBox(lobbyID, adminName, noOfPlayersInLobby, lobbyIsOpen, startOrJoin); + VBox playerList = new VBox(); + TitledPane headParent = new TitledPane(head.toString(), playerList); + + { + head.setAlignment(Pos.CENTER_LEFT); + playerList.setAlignment(Pos.CENTER_LEFT); + headParent.setCollapsible(true); + } + + /** + * The updateItem method should not be called by developers, but it is the + * best method for developers to override to allow for them to customise the + * visuals of the cell. To clarify, developers should never call this method + * in their code (they should leave it up to the UI control, such as the + * {@link ListView} control) to call this method. However, the purpose of + * having the updateItem method is so that developers, when specifying + * custom cell factories (again, like the ListView {@link + * ListView#cellFactoryProperty() cell factory}), the updateItem method can + * be overridden to allow for complete customisation of the cell. + * + *

It is very important that subclasses + * of Cell override the updateItem method properly, as failure to do so will + * lead to issues such as blank cells or cells with unexpected content + * appearing within them. Here is an example of how to properly override the + * updateItem method: + * + *

+         * protected void updateItem(T item, boolean empty) {
+         *     super.updateItem(item, empty);
+         *
+         *     if (empty || item == null) {
+         *         setText(null);
+         *         setGraphic(null);
+         *     } else {
+         *         setText(item.toString());
+         *     }
+         * }
+         * 
+ * + *

Note in this code sample two important points: + *

    + *
  1. We call the super.updateItem(T, boolean) method. If this is not + * done, the item and empty properties are not correctly set, and you are + * likely to end up with graphical issues.
  2. + *
  3. We test for the empty condition, and if true, we + * set the text and graphic properties to null. If we do not do this, + * it is almost guaranteed that end users will see graphical artifacts + * in cells unexpectedly.
  4. + *
+ * @param item The new item for the cell. + * + * @param empty whether or not this cell represents data from the list. If + * it is empty, then it does not represent any domain data, but + * is a cell + */ + @Override + protected void updateItem(LobbyListItem item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setText(null); + setGraphic((null)); + } else { + LOGGER.debug("In ELSE part of LobbyView Update item()"); + lobbyID.setText(item.getLobbyID()); + adminName.setText(item.getAdminName()); + startOrJoin.setOnAction(event -> { + if (item.isOwnedByClient()) { + startGame(); + } else { + joinGame(item.lobbyIDProperty().getName()); + } + }); + startOrJoin.setText(item.isOwnedByClient() ? "Start" : "Join"); + setGraphic(headParent); + } + } + }; + return cell; + }); + LobbyListView.setPlaceholder(new Text("No open lobbies!")); client.getAllClients().addListener(new ListChangeListener() { @Override @@ -182,12 +347,13 @@ public class LoungeSceneViewController implements Initializable { } /** - * Adds players to a lobby "NMEMB" {@link ch.unibas.dmi.dbis.cs108.multiplayer.helpers.GuiParameters} - * + * Adds players to a lobby + * "NMEMB" {@link ch.unibas.dmi.dbis.cs108.multiplayer.helpers.GuiParameters} * @param lobbyID * @param player */ public void addPlayerToLobby(String lobbyID, String player) { + LOGGER.debug("Lobby ID: " + lobbyID + " player: " + player); ObservableList members = lobbyToMemberssMap.get(lobbyID); members.add(player); } @@ -200,39 +366,21 @@ public class LoungeSceneViewController implements Initializable { * @param adminName */ public void newLobby(String lobbyID, String adminName) { + LOGGER.debug("New lobby with ID " + lobbyID + " and admin " + adminName); SimpleStringProperty id = new SimpleStringProperty(lobbyID); SimpleStringProperty admin = new SimpleStringProperty((adminName)); - boolean ownedByClient = false; + Button startOrJoin; + boolean ownedByClient = false; if (adminName.equals(client.getUsername())) { + LOGGER.debug("Client is admin. Name: " + adminName); ownedByClient = true; - startOrJoin = new Button("Start"); - startOrJoin.setOnAction(event -> startGame()); } else { - startOrJoin = new Button("Join"); - startOrJoin.setOnAction(event -> joinGame(lobbyID)); + LOGGER.debug("Different admin case. ADMIN Name: " + adminName); } - HBox lobby = new HBox(); - Label idLabel = new Label(); - Label adminLabel = new Label(); - idLabel.setText(lobbyID); - adminLabel.setText(adminName); - startOrJoin.setVisible(true); - lobby.getChildren().add(idLabel); - lobby.getChildren().add(adminLabel); - lobby.getChildren().add(startOrJoin); - ListView members = new ListView<>(); - members.setId("membersOfLobby"); - if (ownedByClient) { - members.getItems().add("(you are admin) " + adminName); - } else { - members.getItems().add("(admin)" + adminName); - members.getItems().add(client.getUsername()); - } - lobby.setId(lobbyID); - lobbyToMemberssMap.put(lobbyID, members.getItems()); - lobby.setVisible(true); - LobbyListView.getItems().add(lobby); + LobbyListItem item = new LobbyListItem(id, admin, new SimpleBooleanProperty(ownedByClient), + new SimpleBooleanProperty(true), new SimpleIntegerProperty(0)); + lobbies.add(item); } public void joinGame(String lobbyID) { @@ -259,7 +407,7 @@ public class LoungeSceneViewController implements Initializable { * @param s */ public void addClientToList(String s) { - ClientListView.getItems().add(new SimpleStringProperty(s)); + clients.add(new ClientListItem(s)); } public void newGame() { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java index cfdf602..c928208 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/ClientHandler.java @@ -477,6 +477,7 @@ public class ClientHandler implements Runnable { Lobby newGame = new Lobby(this); guiUpdateAll(Protocol.printToGUI + "$" + GuiParameters.newLobbyCreated + "$" + getLobby() .getLobbyID() + ":" + getClientUserName()); + LOGGER.debug("Lobby: " + getLobby().getLobbyID() + ". In method createNewLobby()"); } else { sendAnnouncementToClient("You are already in lobby nr. " + Lobby.clientIsInLobby(this)); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java index 59121c4..5bb18a5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/JServerProtocolParser.java @@ -104,7 +104,7 @@ public class JServerProtocolParser { break; case Protocol.leaveLobby: h.leaveLobby(); - h.sendMsgToClient(Protocol.printToGUI + "$" + GuiParameters.viewChangeToStart + "$"); + h.sendMsgToClient(Protocol.printToGUI + "$" + GuiParameters.viewChangeToLobby + "$"); break; case Protocol.votedFor: LOGGER.debug("Made it here");