Launcher: server list: option to edit and connect

This commit is contained in:
Bixilon 2020-08-26 20:21:06 +02:00
parent ff4445d6b9
commit eb2c1eff63
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 159 additions and 13 deletions

View File

@ -13,6 +13,8 @@
package de.bixilon.minosoft; package de.bixilon.minosoft;
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Version;
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Versions;
import de.bixilon.minosoft.gui.main.GUITools; import de.bixilon.minosoft.gui.main.GUITools;
import de.bixilon.minosoft.gui.main.Server; import de.bixilon.minosoft.gui.main.Server;
import de.bixilon.minosoft.gui.main.ServerListCell; import de.bixilon.minosoft.gui.main.ServerListCell;
@ -24,6 +26,9 @@ import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.util.Comparator;
import java.util.Map;
public class Launcher extends Application { public class Launcher extends Application {
@ -33,6 +38,18 @@ public class Launcher extends Application {
@Override @Override
public void start(Stage primaryStage) { public void start(Stage primaryStage) {
GUITools.versions.add(Versions.getLowestVersionSupported());
for (Map.Entry<Integer, Version> version : Versions.getVersionMap().entrySet()) {
GUITools.versions.add(version.getValue());
}
Comparator<Version> comparator = Comparator.comparingInt(Version::getProtocolVersion);
FXCollections.sort(GUITools.versions, comparator);
GUITools.versions.sort((a, b) -> {
if (a.getProtocolVersion() == -1) {
return -Integer.MAX_VALUE;
}
return (b.getProtocolVersion() - a.getProtocolVersion());
});
ListView<Server> listView = new ListView<>(); ListView<Server> listView = new ListView<>();
listView.setCellFactory((lv) -> ServerListCell.newInstance()); listView.setCellFactory((lv) -> ServerListCell.newInstance());
@ -40,7 +57,7 @@ public class Launcher extends Application {
servers.addAll(Minosoft.serverList); servers.addAll(Minosoft.serverList);
listView.setItems(servers); listView.setItems(servers);
Scene scene = new Scene(new BorderPane(listView), 400, 450); Scene scene = new Scene(new BorderPane(listView), 550, 800);
primaryStage.setScene(scene); primaryStage.setScene(scene);
primaryStage.setTitle("Minosoft"); primaryStage.setTitle("Minosoft");
primaryStage.getIcons().add(GUITools.logo); primaryStage.getIcons().add(GUITools.logo);

View File

@ -81,6 +81,9 @@ public class Version {
if (super.equals(obj)) { if (super.equals(obj)) {
return true; return true;
} }
if (obj == null) {
return false;
}
if (hashCode() != obj.hashCode()) { if (hashCode() != obj.hashCode()) {
return false; return false;
} }

View File

@ -151,6 +151,10 @@ public class Versions {
} }
public static Version getLowestVersionSupported() { public static Version getLowestVersionSupported() {
return new Version("13w41b", 0, null, null); return new Version("Automatic", -1, null, null);
}
public static HashBiMap<Integer, Version> getVersionMap() {
return versionMap;
} }
} }

View File

@ -13,6 +13,9 @@
package de.bixilon.minosoft.gui.main; package de.bixilon.minosoft.gui.main;
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Version;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -20,6 +23,7 @@ import java.util.Base64;
public class GUITools { public class GUITools {
public final static Image logo = new Image(GUITools.class.getResourceAsStream("/icons/windowIcon.png")); public final static Image logo = new Image(GUITools.class.getResourceAsStream("/icons/windowIcon.png"));
public final static ObservableList<Version> versions = FXCollections.observableArrayList();
public static Image getImageFromBase64(String base64) { public static Image getImageFromBase64(String base64) {
if (base64 == null) { if (base64 == null) {

View File

@ -17,19 +17,24 @@ import de.bixilon.minosoft.Minosoft;
import de.bixilon.minosoft.game.datatypes.Player; import de.bixilon.minosoft.game.datatypes.Player;
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Version; import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Version;
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Versions; import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Versions;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.protocol.network.Connection; import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.protocol.ConnectionReasons; import de.bixilon.minosoft.protocol.protocol.ConnectionReasons;
import de.bixilon.minosoft.util.DNSUtil;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Label; import javafx.geometry.Insets;
import javafx.scene.control.ListCell; import javafx.scene.Node;
import javafx.scene.control.MenuItem; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.util.Callback;
import javafx.util.Pair;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -57,7 +62,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
private Label serverName; private Label serverName;
@FXML @FXML
private AnchorPane root; private AnchorPane root;
private Server model; private Server server;
public static ServerListCell newInstance() { public static ServerListCell newInstance() {
FXMLLoader loader = new FXMLLoader(ServerListCell.class.getResource("/layout/cells/server.fxml")); FXMLLoader loader = new FXMLLoader(ServerListCell.class.getResource("/layout/cells/server.fxml"));
@ -87,15 +92,21 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
protected void updateItem(Server server, boolean empty) { protected void updateItem(Server server, boolean empty) {
super.updateItem(server, empty); super.updateItem(server, empty);
getRoot().getChildrenUnmodifiable().forEach(c -> c.setVisible(!empty)); getRoot().getChildrenUnmodifiable().forEach(c -> c.setVisible(!empty));
if (!empty && server != null && !server.equals(this.model)) { if (!empty && server != null && !server.equals(this.server)) {
this.server = server;
serverName.setText(server.getName()); serverName.setText(server.getName());
Image favicon = server.getFavicon(); Image favicon = server.getFavicon();
if (favicon == null) { if (favicon == null) {
favicon = GUITools.logo; favicon = GUITools.logo;
} }
icon.setImage(favicon); icon.setImage(favicon);
optionsConnect.setOnAction(e -> {
Connection connection = new Connection(Connection.lastConnectionId++, server.getAddress(), new Player(Minosoft.accountList.get(0)));
connection.resolve(ConnectionReasons.CONNECT);
});
optionsEdit.setOnAction(e -> edit());
Connection connection = new Connection(server.getId(), server.getAddress(), new Player(Minosoft.accountList.get(0))); Connection connection = new Connection(Connection.lastConnectionId++, server.getAddress(), null);
connection.addPingCallback(ping -> Platform.runLater(() -> { connection.addPingCallback(ping -> Platform.runLater(() -> {
if (ping == null) { if (ping == null) {
// Offline // Offline
@ -105,7 +116,12 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
return; return;
} }
players.setText(String.format("%d/%d", ping.getPlayerOnline(), ping.getMaxPlayers())); players.setText(String.format("%d/%d", ping.getPlayerOnline(), ping.getMaxPlayers()));
Version serverVersion = Versions.getVersionById(ping.getProtocolNumber()); Version serverVersion;
if (server.getDesiredVersion() == -1) {
serverVersion = Versions.getVersionById(ping.getProtocolNumber());
} else {
serverVersion = Versions.getVersionById(server.getDesiredVersion());
}
if (serverVersion == null) { if (serverVersion == null) {
version.setText(ping.getServerVersion()); version.setText(ping.getServerVersion());
version.setTextFill(Color.RED); version.setTextFill(Color.RED);
@ -120,13 +136,93 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
icon.setImage(ping.getFavicon()); icon.setImage(ping.getFavicon());
} }
})); }));
connection.resolve(ConnectionReasons.PING); // resolve dns address and connect connection.resolve(ConnectionReasons.PING); // resolve dns address and ping
} }
this.model = server;
} }
@Override @Override
public void updateSelected(boolean selected) { public void updateSelected(boolean selected) {
super.updateSelected(selected); super.updateSelected(selected);
} }
public void edit() {
// Create the custom dialog.
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("Edit server: " + server.getName());
dialog.setHeaderText("Edit the details of the server");
// Set the button types.
ButtonType loginButtonType = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);
// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 300, 10, 10));
TextField serverName = new TextField();
serverName.setPromptText("Servername");
serverName.setText(server.getName());
TextField serverAddress = new TextField();
serverAddress.setPromptText("Server address");
serverAddress.setText(server.getAddress());
ComboBox<Version> versionList = new ComboBox<>(GUITools.versions);
versionList.setCellFactory(new Callback<>() {
@Override
public ListCell<Version> call(ListView<Version> p) {
return new ListCell<>() {
@Override
protected void updateItem(Version version, boolean empty) {
super.updateItem(version, empty);
if (!empty && version != null) {
setText(String.format("%s (%d)", version.getVersionName(), version.getProtocolVersion()));
}
}
};
}
});
if (server.getDesiredVersion() == -1) {
versionList.getSelectionModel().select(Versions.getLowestVersionSupported());
} else {
versionList.getSelectionModel().select(Versions.getVersionById(server.getDesiredVersion()));
}
grid.add(new Label("Servername:"), 0, 0);
grid.add(serverName, 1, 0);
grid.add(new Label("Server address:"), 0, 1);
grid.add(serverAddress, 1, 1);
grid.add(new Label("Version:"), 0, 2);
grid.add(versionList, 1, 2);
// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
// Do some validation (using the Java 8 lambda syntax).
serverAddress.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty());
});
dialog.getDialogPane().setContent(grid);
// Request focus on the username field by default.
Platform.runLater(serverName::requestFocus);
// Convert the result to a username-password-pair when the login button is clicked.
dialog.setResultConverter(dialogButton -> {
if (dialogButton == loginButtonType) {
ServerListCell.this.serverName.setText(serverName.getText());
ServerListCell.this.server.setName(serverName.getText());
ServerListCell.this.server.setDesiredVersion(versionList.getSelectionModel().getSelectedItem().getProtocolVersion());
ServerListCell.this.server.setAddress(DNSUtil.correctHostName(serverAddress.getText()));
ServerListCell.this.server.saveToConfig();
Log.info(String.format("Edited and saved server (serverName=%s, serverAddress=%s, version=%d)", server.getName(), server.getAddress(), server.getDesiredVersion()));
}
return null;
});
dialog.showAndWait();
}
} }

View File

@ -41,6 +41,7 @@ import org.xbill.DNS.TextParseException;
import java.util.ArrayList; import java.util.ArrayList;
public class Connection { public class Connection {
public static int lastConnectionId;
final ArrayList<ServerAddress> addresses; final ArrayList<ServerAddress> addresses;
final Network network = new Network(this); final Network network = new Network(this);
final PacketHandler handler = new PacketHandler(this); final PacketHandler handler = new PacketHandler(this);
@ -352,4 +353,5 @@ public class Connection {
callback.handle(ping); callback.handle(ping);
} }
} }
} }

View File

@ -54,7 +54,7 @@ public class InByteBuffer {
public byte readByte() { public byte readByte() {
byte ret; byte ret;
ret = bytes[pos]; ret = bytes[pos];
pos = pos + 1; pos++;
return ret; return ret;
} }

View File

@ -52,7 +52,7 @@ public final class DNSUtil {
} }
public String correctHostName(String hostname) { public static String correctHostName(String hostname) {
// replaces invalid chars to avoid copy and paste issues (like spaces, ...) // replaces invalid chars to avoid copy and paste issues (like spaces, ...)
hostname = hostname.replaceAll("\\s", ""); hostname = hostname.replaceAll("\\s", "");
return hostname; return hostname;

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml/1" prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/11.0.1">
<MenuBar VBox.vgrow="NEVER">
<Menu mnemonicParsing="false" text="File">
<MenuItem mnemonicParsing="false" text="Preferences…"/>
<SeparatorMenuItem mnemonicParsing="false"/>
<MenuItem mnemonicParsing="false" text="Quit"/>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<MenuItem id="menu_help_about" mnemonicParsing="false" text="About"/>
</Menu>
</MenuBar>
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
<ListView id="servers" layoutX="14.0" layoutY="14.0" prefHeight="327.0" prefWidth="402.0"/>
</AnchorPane>
</VBox>