diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/Player.java b/src/main/java/de/bixilon/minosoft/game/datatypes/Player.java index e5d8e28c3..b3f3c5bd3 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/Player.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/Player.java @@ -30,7 +30,7 @@ import java.util.UUID; import static de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.PLAYER_INVENTORY_ID; public class Player { - final MojangAccount acc; + final MojangAccount account; final ScoreboardManager scoreboardManager = new ScoreboardManager(); public final HashMap playerList = new HashMap<>(); float health; @@ -49,22 +49,22 @@ public class Player { TextComponent tabHeader; TextComponent tabFooter; - public Player(MojangAccount acc) { - this.acc = acc; + public Player(MojangAccount account) { + this.account = account; // create our own inventory without any properties inventories.put(PLAYER_INVENTORY_ID, new Inventory(null)); } public String getPlayerName() { - return acc.getPlayerName(); + return account.getPlayerName(); } public UUID getPlayerUUID() { - return acc.getUUID(); + return account.getUUID(); } public MojangAccount getAccount() { - return this.acc; + return this.account; } public float getHealth() { diff --git a/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java b/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java index 2a8a5f1ff..131b80f05 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java @@ -112,6 +112,7 @@ public class AccountListCell extends ListCell implements Initiali public void select() { Minosoft.selectAccount(account); listView.refresh(); + ServerListCell.listView.refresh(); } private void resetCell() { diff --git a/src/main/java/de/bixilon/minosoft/gui/main/ConnectionChangeCallback.java b/src/main/java/de/bixilon/minosoft/gui/main/ConnectionChangeCallback.java new file mode 100644 index 000000000..163d2a09f --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/main/ConnectionChangeCallback.java @@ -0,0 +1,20 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * 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 3 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, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.main; + +import de.bixilon.minosoft.protocol.network.Connection; + +public interface ConnectionChangeCallback { + void handle(Connection connection); +} diff --git a/src/main/java/de/bixilon/minosoft/gui/main/Server.java b/src/main/java/de/bixilon/minosoft/gui/main/Server.java index 5c365e051..0cc1f91cf 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/Server.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/Server.java @@ -20,6 +20,7 @@ import de.bixilon.minosoft.protocol.protocol.ConnectionReasons; import javafx.scene.image.Image; import javax.annotation.Nullable; +import java.util.ArrayList; public class Server { static int highestServerId; @@ -29,6 +30,7 @@ public class Server { int desiredVersion; String favicon; Connection lastPing; + ArrayList connections = new ArrayList<>(); public Server(int id, String name, String address, int desiredVersion) { this.id = id; @@ -121,4 +123,21 @@ public class Server { } lastPing.resolve(ConnectionReasons.PING, getDesiredVersion()); // resolve dns address and ping } + + public ArrayList getConnections() { + return connections; + } + + public void addConnection(Connection connection) { + connections.add(connection); + } + + public boolean isConnected() { + for (Connection connection : connections) { + if (connection.isConnected()) { + return true; + } + } + return false; + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java b/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java index 333bccbcd..8df78cfb8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java @@ -28,6 +28,8 @@ import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.geometry.Insets; import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -35,6 +37,8 @@ import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; +import javafx.stage.Modality; +import javafx.stage.Stage; import javafx.util.Pair; import java.io.IOException; @@ -59,6 +63,8 @@ public class ServerListCell extends ListCell implements Initializable { public MenuButton optionsMenu; @FXML public Label serverBrand; + @FXML + public MenuItem optionsSessions; boolean canConnect = false; @FXML private Label serverName; @@ -114,6 +120,12 @@ public class ServerListCell extends ListCell implements Initializable { favicon = GUITools.logo; } icon.setImage(favicon); + if (server.isConnected()) { + setStyle("-fx-background-color: darkseagreen;"); + optionsSessions.setDisable(false); + } else { + optionsSessions.setDisable(true); + } if (server.getLastPing() == null) { server.ping(); } @@ -123,6 +135,10 @@ public class ServerListCell extends ListCell implements Initializable { return; } resetCell(); + + if (server.isConnected()) { + setStyle("-fx-background-color: darkseagreen;"); + } if (ping == null) { // Offline players.setText(""); @@ -257,10 +273,14 @@ public class ServerListCell extends ListCell implements Initializable { } else { version = Versions.getVersionById(server.getDesiredVersion()); } + optionsConnect.setDisable(true); connection.connect(server.getLastPing().getAddress(), version); - setStyle("-fx-background-color: darkseagreen;"); + connection.addConnectionChangeCallback(this::handleConnectionCallback); + server.addConnection(connection); + } + private void resetCell() { // clear all cells setStyle(null); @@ -384,4 +404,43 @@ public class ServerListCell extends ListCell implements Initializable { dialog.showAndWait(); } + + public void manageSessions() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource("/layout/sessions.fxml")); + Parent parent = loader.load(); + ((SessionsWindow) loader.getController()).setServer(server); + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setTitle(String.format("Sessions - %s - Minosoft", server.getName())); + stage.setScene(new Scene(parent)); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void handleConnectionCallback(Connection connection) { + if (!server.getConnections().contains(connection)) { + // the card got recycled + return; + } + Platform.runLater(() -> { + if (!connection.isConnected()) { + // maybe we got disconnected + if (!server.isConnected()) { + setStyle(null); + optionsSessions.setDisable(true); + optionsConnect.setDisable(false); + } + return; + } + + if (Minosoft.getSelectedAccount() != connection.getPlayer().getAccount()) { + optionsConnect.setDisable(false); + } + setStyle("-fx-background-color: darkseagreen;"); + optionsSessions.setDisable(false); + }); + } } \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/gui/main/SessionListCell.java b/src/main/java/de/bixilon/minosoft/gui/main/SessionListCell.java new file mode 100644 index 000000000..f1c9492a1 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/main/SessionListCell.java @@ -0,0 +1,111 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * 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 3 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, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.main; + +import de.bixilon.minosoft.protocol.network.Connection; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +public class SessionListCell extends ListCell implements Initializable { + public static ListView listView = new ListView<>(); + @FXML + public Label account; + @FXML + public Label connectionId; + @FXML + private AnchorPane root; + private Connection connection; + + public static SessionListCell newInstance() { + FXMLLoader loader = new FXMLLoader(SessionListCell.class.getResource("/layout/cells/session.fxml")); + try { + loader.load(); + return loader.getController(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + updateSelected(false); + setGraphic(root); + } + + public AnchorPane getRoot() { + return root; + } + + + @Override + protected void updateItem(Connection connection, boolean empty) { + super.updateItem(connection, empty); + + root.setVisible(!empty); + if (empty) { + return; + } + + if (connection == null) { + return; + } + + if (connection.equals(this.connection)) { + return; + } + setStyle(null); + this.connection = connection; + connection.addConnectionChangeCallback(this::handleConnectionCallback); + connectionId.setText(String.format("#%d", connection.getConnectionId())); + account.setText(connection.getPlayer().getAccount().getPlayerName()); + } + + @Override + public void updateSelected(boolean selected) { + super.updateSelected(selected); + } + + public void disconnect() { + setStyle("-fx-background-color: indianred"); + connection.disconnect(); + } + + private void handleConnectionCallback(Connection connection) { + if (this.connection != connection) { + // the card got recycled + return; + } + + if (!connection.isConnected()) { + Platform.runLater(() -> { + listView.getItems().remove(connection); + if (listView.getItems().size() == 0) { + ((Stage) root.getScene().getWindow()).close(); + } + }); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/gui/main/SessionsWindow.java b/src/main/java/de/bixilon/minosoft/gui/main/SessionsWindow.java new file mode 100644 index 000000000..af51c0a94 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/main/SessionsWindow.java @@ -0,0 +1,52 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * 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 3 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, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.main; + +import de.bixilon.minosoft.protocol.network.Connection; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.layout.BorderPane; + +import java.net.URL; +import java.util.ResourceBundle; + +public class SessionsWindow implements Initializable { + @FXML + public BorderPane accountPane; + Server server; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + SessionListCell.listView.setCellFactory((lv) -> SessionListCell.newInstance()); + } + + public void setServer(Server server) { + this.server = server; + ObservableList connections = FXCollections.observableArrayList(); + for (Connection connection : server.getConnections()) { + if (!connection.isConnected()) { + server.getConnections().remove(connection); + } + connections.add(connection); + } + SessionListCell.listView.setItems(connections); + accountPane.setCenter(SessionListCell.listView); + } + + public void disconnectAll() { + server.getConnections().forEach(Connection::disconnect); + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java index 155103673..fdd5dfd8d 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java +++ b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java @@ -22,6 +22,7 @@ import de.bixilon.minosoft.game.datatypes.objectLoader.CustomMapping; import de.bixilon.minosoft.game.datatypes.objectLoader.recipes.Recipes; import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Version; import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Versions; +import de.bixilon.minosoft.gui.main.ConnectionChangeCallback; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.logging.LogLevels; import de.bixilon.minosoft.ping.ServerListPing; @@ -52,6 +53,7 @@ public class Connection { final LinkedBlockingQueue handlingQueue = new LinkedBlockingQueue<>(); final VelocityHandler velocityHandler = new VelocityHandler(this); final HashSet pingCallbacks = new HashSet<>(); + final HashSet connectionChangeCallbacks = new HashSet<>(); final int connectionId; final Player player; final String hostname; @@ -197,13 +199,15 @@ public class Connection { resolve(address); } else { // no connection and no servers available anymore... sorry, but you can not play today :( - handleCallbacks(null); + handlePingCallbacks(null); } break; case FAILED_NO_RETRY: - handleCallbacks(null); + handlePingCallbacks(null); break; } + // handle callbacks + connectionChangeCallbacks.forEach((callback -> callback.handle(this))); } public Version getVersion() { @@ -361,13 +365,13 @@ public class Connection { return connectionId; } - public void addPingCallback(PingCallback pingCallback) { + public void addPingCallback(PingCallback callback) { if (getConnectionState() == ConnectionStates.FAILED || getConnectionState() == ConnectionStates.FAILED_NO_RETRY || lastPing != null) { // ping done - pingCallback.handle(lastPing); + callback.handle(lastPing); return; } - pingCallbacks.add(pingCallback); + pingCallbacks.add(callback); } public HashSet getPingCallbacks() { @@ -382,17 +386,23 @@ public class Connection { this.desiredVersionNumber = desiredVersionNumber; } - public void handleCallbacks(ServerListPing ping) { + public void handlePingCallbacks(ServerListPing ping) { this.lastPing = ping; - for (PingCallback callback : getPingCallbacks()) { - callback.handle(ping); - } + pingCallbacks.forEach((callback -> callback.handle(ping))); } public Exception getLastConnectionException() { return network.lastException; } + public void addConnectionChangeCallback(ConnectionChangeCallback callback) { + connectionChangeCallbacks.add(callback); + } + + public HashSet getConnectionChangeCallbacks() { + return connectionChangeCallbacks; + } + public ServerListPing getLastPing() { return lastPing; } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java index 2ee149c9c..9b0cfb095 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java @@ -68,7 +68,7 @@ public class PacketHandler { connection.setVersion(version); } Log.info(String.format("Status response received: %s/%s online. MotD: '%s'", pkg.getResponse().getPlayerOnline(), pkg.getResponse().getMaxPlayers(), pkg.getResponse().getMotd().getColoredMessage())); - connection.handleCallbacks(pkg.getResponse()); + connection.handlePingCallbacks(pkg.getResponse()); } public void handle(PacketStatusPong pkg) { diff --git a/src/main/resources/layout/cells/server.fxml b/src/main/resources/layout/cells/server.fxml index 8fb383a61..9236753d1 100644 --- a/src/main/resources/layout/cells/server.fxml +++ b/src/main/resources/layout/cells/server.fxml @@ -31,6 +31,8 @@ + diff --git a/src/main/resources/layout/cells/session.fxml b/src/main/resources/layout/cells/session.fxml new file mode 100644 index 000000000..2b907a365 --- /dev/null +++ b/src/main/resources/layout/cells/session.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/src/main/resources/layout/sessions.fxml b/src/main/resources/layout/sessions.fxml new file mode 100644 index 000000000..0c7bd5ba5 --- /dev/null +++ b/src/main/resources/layout/sessions.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + +