Launcher: manage active connections to a server (=sessions)

This commit is contained in:
Bixilon 2020-09-06 16:28:41 +02:00
parent f7dd739aad
commit 744189c745
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 338 additions and 17 deletions

View File

@ -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<UUID, PlayerListItem> 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() {

View File

@ -112,6 +112,7 @@ public class AccountListCell extends ListCell<MojangAccount> implements Initiali
public void select() {
Minosoft.selectAccount(account);
listView.refresh();
ServerListCell.listView.refresh();
}
private void resetCell() {

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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);
}

View File

@ -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<Connection> 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<Connection> 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;
}
}

View File

@ -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<Server> 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<Server> 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<Server> 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<Server> 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<Server> 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);
});
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<Connection> implements Initializable {
public static ListView<Connection> 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();
}
});
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<Connection> 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);
}
}

View File

@ -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<ClientboundPacket> handlingQueue = new LinkedBlockingQueue<>();
final VelocityHandler velocityHandler = new VelocityHandler(this);
final HashSet<PingCallback> pingCallbacks = new HashSet<>();
final HashSet<ConnectionChangeCallback> 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<PingCallback> 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<ConnectionChangeCallback> getConnectionChangeCallbacks() {
return connectionChangeCallbacks;
}
public ServerListPing getLastPing() {
return lastPing;
}

View File

@ -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) {

View File

@ -31,6 +31,8 @@
<MenuItem mnemonicParsing="false" onAction="#showInfo" text="Info"/>
<MenuItem mnemonicParsing="false" onAction="#edit" text="Edit"/>
<MenuItem mnemonicParsing="false" onAction="#refresh" text="Refresh"/>
<MenuItem fx:id="optionsSessions" mnemonicParsing="false" onAction="#manageSessions" disable="true"
text="Sessions"/>
<MenuItem mnemonicParsing="false" style="-fx-text-fill: red" onAction="#delete" text="Delete"/>
</items>
</MenuButton>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="root" maxHeight="-Infinity" maxWidth="500.0"
minHeight="-Infinity" minWidth="500.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/11.0.1"
fx:controller="de.bixilon.minosoft.gui.main.SessionListCell">
<Label fx:id="connectionId" layoutX="111.0" layoutY="14.0" maxWidth="200.0" minWidth="10.0" text="#1"
underline="true" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="5.0">
<font>
<Font size="17.0"/>
</font>
</Label>
<Label fx:id="account" layoutX="408.0" layoutY="8.0" text="account" AnchorPane.bottomAnchor="5.0"
AnchorPane.leftAnchor="100.0" AnchorPane.topAnchor="5.0"/>
<MenuButton layoutX="389.0" layoutY="81.0" mnemonicParsing="false" text="⋮" AnchorPane.bottomAnchor="5.0"
AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0">
<items>
<MenuItem mnemonicParsing="false" onAction="#disconnect" style="-fx-text-fill: red" text="Disconnect"/>
</items>
</MenuButton>
</AnchorPane>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/11.0.1"
fx:controller="de.bixilon.minosoft.gui.main.SessionsWindow">
<MenuBar VBox.vgrow="NEVER">
<Menu mnemonicParsing="false" text="_Disconnect">
<MenuItem mnemonicParsing="false" onAction="#disconnectAll" text="From all"/>
</Menu>
</MenuBar>
<AnchorPane VBox.vgrow="ALWAYS">
<ScrollPane fitToHeight="true" fitToWidth="true" layoutX="14.0" layoutY="14.0" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="500.0" prefWidth="500.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<BorderPane fx:id="accountPane"/>
</ScrollPane>
</AnchorPane>
</VBox>