mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-14 18:05:51 -04:00
modding: improve code and performance of callback event invoker, convert ServerListCell to kotlin
This commit is contained in:
parent
31904238aa
commit
0d07a308c8
@ -39,7 +39,7 @@ public class Server {
|
|||||||
private int desiredVersion;
|
private int desiredVersion;
|
||||||
private byte[] favicon;
|
private byte[] favicon;
|
||||||
private StatusConnection lastPing;
|
private StatusConnection lastPing;
|
||||||
private boolean readOnly;
|
private boolean temporary;
|
||||||
private ServerListCell cell;
|
private ServerListCell cell;
|
||||||
|
|
||||||
public Server(int id, ChatComponent name, String address, int desiredVersion, byte[] favicon) {
|
public Server(int id, ChatComponent name, String address, int desiredVersion, byte[] favicon) {
|
||||||
@ -67,7 +67,7 @@ public class Server {
|
|||||||
this.name = ChatComponent.Companion.valueOf(String.format("LAN Server #%d", LANServerListener.getServerMap().size()));
|
this.name = ChatComponent.Companion.valueOf(String.format("LAN Server #%d", LANServerListener.getServerMap().size()));
|
||||||
this.address = address.toString();
|
this.address = address.toString();
|
||||||
this.desiredVersion = -1; // Automatic
|
this.desiredVersion = -1; // Automatic
|
||||||
this.readOnly = true;
|
this.temporary = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getNextServerId() {
|
public static int getNextServerId() {
|
||||||
@ -96,7 +96,7 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void saveToConfig() {
|
public void saveToConfig() {
|
||||||
if (isReadOnly()) {
|
if (isTemporary()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Minosoft.getConfig().getConfig().getServer().getEntries().put(this.getId(), this);
|
Minosoft.getConfig().getConfig().getServer().getEntries().put(this.getId(), this);
|
||||||
@ -104,7 +104,7 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (isReadOnly()) {
|
if (isTemporary()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Minosoft.getConfig().getConfig().getServer().getEntries().remove(this.getId());
|
Minosoft.getConfig().getConfig().getServer().getEntries().remove(this.getId());
|
||||||
@ -197,8 +197,8 @@ public class Server {
|
|||||||
return Base64.getEncoder().encodeToString(this.favicon);
|
return Base64.getEncoder().encodeToString(this.favicon);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReadOnly() {
|
public boolean isTemporary() {
|
||||||
return this.readOnly;
|
return this.temporary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerListCell getCell() {
|
public ServerListCell getCell() {
|
||||||
|
@ -1,451 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 com.jfoenix.controls.JFXAlert;
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
|
||||||
import com.jfoenix.controls.JFXDialogLayout;
|
|
||||||
import de.bixilon.minosoft.Minosoft;
|
|
||||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
|
||||||
import de.bixilon.minosoft.data.locale.Strings;
|
|
||||||
import de.bixilon.minosoft.data.mappings.ResourceLocation;
|
|
||||||
import de.bixilon.minosoft.data.mappings.versions.Version;
|
|
||||||
import de.bixilon.minosoft.data.mappings.versions.Versions;
|
|
||||||
import de.bixilon.minosoft.data.player.tab.PingBars;
|
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent;
|
|
||||||
import de.bixilon.minosoft.modding.event.CallbackEventInvoker;
|
|
||||||
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent;
|
|
||||||
import de.bixilon.minosoft.modding.event.events.ServerListPongEvent;
|
|
||||||
import de.bixilon.minosoft.modding.event.events.ServerListStatusArriveEvent;
|
|
||||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection;
|
|
||||||
import de.bixilon.minosoft.protocol.ping.ForgeModInfo;
|
|
||||||
import de.bixilon.minosoft.protocol.ping.ServerListPing;
|
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
|
||||||
import de.bixilon.minosoft.util.CountUpAndDownLatch;
|
|
||||||
import de.bixilon.minosoft.util.logging.Log;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.*;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.stage.Modality;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public class ServerListCell extends ListCell<Server> implements Initializable {
|
|
||||||
public static final ListView<Server> SERVER_LIST_VIEW = new ListView<>();
|
|
||||||
|
|
||||||
public HBox hBox;
|
|
||||||
public GridPane root;
|
|
||||||
|
|
||||||
public ImageView faviconField;
|
|
||||||
public TextFlow nameField;
|
|
||||||
public TextFlow motdField;
|
|
||||||
public Label versionField;
|
|
||||||
public Label playersField;
|
|
||||||
public Label brandField;
|
|
||||||
|
|
||||||
public MenuItem optionsConnect;
|
|
||||||
public MenuItem optionsShowInfo;
|
|
||||||
public MenuItem optionsEdit;
|
|
||||||
public MenuItem optionsRefresh;
|
|
||||||
public MenuItem optionsSessions;
|
|
||||||
public MenuItem optionsDelete;
|
|
||||||
public MenuButton optionsMenu;
|
|
||||||
public Label pingField;
|
|
||||||
|
|
||||||
private boolean canConnect;
|
|
||||||
private Server server;
|
|
||||||
|
|
||||||
public static ServerListCell newInstance() {
|
|
||||||
FXMLLoader loader = new FXMLLoader(Minosoft.MINOSOFT_ASSETS_MANAGER.getAssetURL(new ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "layout/cells/server.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(this.root);
|
|
||||||
|
|
||||||
// change locale
|
|
||||||
this.optionsConnect.setText(LocaleManager.translate(Strings.SERVER_ACTION_CONNECT));
|
|
||||||
this.optionsShowInfo.setText(LocaleManager.translate(Strings.SERVER_ACTION_SHOW_INFO));
|
|
||||||
this.optionsEdit.setText(LocaleManager.translate(Strings.SERVER_ACTION_EDIT));
|
|
||||||
this.optionsRefresh.setText(LocaleManager.translate(Strings.SERVER_ACTION_REFRESH));
|
|
||||||
this.optionsSessions.setText(LocaleManager.translate(Strings.SERVER_ACTION_SESSIONS));
|
|
||||||
this.optionsDelete.setText(LocaleManager.translate(Strings.SERVER_ACTION_DELETE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateItem(Server server, boolean empty) {
|
|
||||||
super.updateItem(server, empty);
|
|
||||||
|
|
||||||
this.root.setVisible(server != null || !empty);
|
|
||||||
this.hBox.setVisible(server != null || !empty);
|
|
||||||
if (empty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.server != server) {
|
|
||||||
resetCell();
|
|
||||||
}
|
|
||||||
server.setCell(this);
|
|
||||||
|
|
||||||
this.server = server;
|
|
||||||
setName(server.getName());
|
|
||||||
|
|
||||||
Image favicon = GUITools.getImage(server.getFavicon());
|
|
||||||
if (favicon == null) {
|
|
||||||
favicon = GUITools.MINOSOFT_LOGO;
|
|
||||||
}
|
|
||||||
this.faviconField.setImage(favicon);
|
|
||||||
if (server.isConnected()) {
|
|
||||||
this.root.getStyleClass().add("list-cell-connected");
|
|
||||||
this.optionsSessions.setDisable(false);
|
|
||||||
} else {
|
|
||||||
this.optionsSessions.setDisable(true);
|
|
||||||
}
|
|
||||||
if (server.getLastPing() == null) {
|
|
||||||
server.ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
server.getLastPing().registerEvent(new CallbackEventInvoker<ServerListStatusArriveEvent>(event -> Platform.runLater(() -> {
|
|
||||||
ServerListPing ping = event.getServerListPing();
|
|
||||||
if (server != this.server) {
|
|
||||||
// cell does not contains us anymore
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resetCell();
|
|
||||||
|
|
||||||
if (server.isConnected()) {
|
|
||||||
this.root.getStyleClass().add("list-cell-connected");
|
|
||||||
}
|
|
||||||
if (ping == null) {
|
|
||||||
// Offline
|
|
||||||
this.playersField.setText("");
|
|
||||||
this.versionField.setText(LocaleManager.translate(Strings.OFFLINE));
|
|
||||||
this.versionField.getStyleClass().add("version-error");
|
|
||||||
setErrorMotd(String.format("%s", server.getLastPing().getLastException()));
|
|
||||||
this.optionsConnect.setDisable(true);
|
|
||||||
this.canConnect = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.playersField.setText(LocaleManager.translate(Strings.SERVER_INFO_SLOTS_PLAYERS_ONLINE, ping.getPlayerOnline(), ping.getMaxPlayers()));
|
|
||||||
Version serverVersion;
|
|
||||||
if (server.getDesiredVersionId() == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
|
||||||
serverVersion = Versions.getVersionByProtocolId(ping.getProtocolId());
|
|
||||||
} else {
|
|
||||||
serverVersion = Versions.getVersionById(server.getDesiredVersionId());
|
|
||||||
this.versionField.setStyle("-fx-text-fill: -secondary-light-light-color;");
|
|
||||||
}
|
|
||||||
if (serverVersion == null) {
|
|
||||||
this.versionField.setText(ping.getServerBrand());
|
|
||||||
this.versionField.setStyle("-fx-text-fill: red;");
|
|
||||||
this.optionsConnect.setDisable(true);
|
|
||||||
this.canConnect = false;
|
|
||||||
} else {
|
|
||||||
this.versionField.setText(serverVersion.getVersionName());
|
|
||||||
this.optionsConnect.setDisable(false);
|
|
||||||
this.canConnect = true;
|
|
||||||
}
|
|
||||||
this.brandField.setText(ping.getServerModInfo().getBrand());
|
|
||||||
this.brandField.setTooltip(new Tooltip(ping.getServerModInfo().getInfo()));
|
|
||||||
this.motdField.getChildren().setAll(ping.getMotd().getJavaFXText());
|
|
||||||
if (ping.getFavicon() != null) {
|
|
||||||
this.faviconField.setImage(GUITools.getImage(ping.getFavicon()));
|
|
||||||
if (!Arrays.equals(ping.getFavicon(), server.getFavicon())) {
|
|
||||||
server.setFavicon(ping.getFavicon());
|
|
||||||
server.saveToConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (server.isReadOnly()) {
|
|
||||||
this.optionsEdit.setDisable(true);
|
|
||||||
this.optionsDelete.setDisable(true);
|
|
||||||
}
|
|
||||||
if (server.getLastPing().getLastException() != null) {
|
|
||||||
// connection failed because of an error in minosoft, but ping was okay
|
|
||||||
this.versionField.setStyle("-fx-text-fill: red;");
|
|
||||||
this.optionsConnect.setDisable(true);
|
|
||||||
this.canConnect = false;
|
|
||||||
setErrorMotd(String.format("%s: %s", server.getLastPing().getLastException().getClass().getCanonicalName(), server.getLastPing().getLastException().getMessage()));
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
server.getLastPing().registerEvent(new CallbackEventInvoker<ServerListPongEvent>(event -> Platform.runLater(() -> {
|
|
||||||
this.pingField.setText(String.format("%dms", event.getLatency()));
|
|
||||||
switch (PingBars.byPing(event.getLatency())) {
|
|
||||||
case BARS_5 -> this.pingField.getStyleClass().add("ping-5-bars");
|
|
||||||
case BARS_4 -> this.pingField.getStyleClass().add("ping-4-bars");
|
|
||||||
case BARS_3 -> this.pingField.getStyleClass().add("ping-3-bars");
|
|
||||||
case BARS_2 -> this.pingField.getStyleClass().add("ping-2-bars");
|
|
||||||
case BARS_1 -> this.pingField.getStyleClass().add("ping-1-bars");
|
|
||||||
case NO_CONNECTION -> this.pingField.getStyleClass().add("ping-no-connection");
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(ChatComponent name) {
|
|
||||||
this.nameField.getChildren().setAll(name.getJavaFXText());
|
|
||||||
for (Node node : this.nameField.getChildren()) {
|
|
||||||
node.setStyle("-fx-font-size: 15pt ;");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetCell() {
|
|
||||||
// clear all cells
|
|
||||||
setStyle(null);
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-connected");
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-connecting");
|
|
||||||
this.motdField.getChildren().clear();
|
|
||||||
this.brandField.setText("");
|
|
||||||
this.brandField.setTooltip(null);
|
|
||||||
this.motdField.setStyle(null);
|
|
||||||
this.versionField.setText(LocaleManager.translate(Strings.CONNECTING));
|
|
||||||
this.versionField.getStyleClass().removeAll("version-error");
|
|
||||||
this.versionField.setStyle(null);
|
|
||||||
this.playersField.setText("");
|
|
||||||
this.pingField.setText("");
|
|
||||||
this.pingField.getStyleClass().removeIf((s -> s.startsWith("ping")));
|
|
||||||
this.optionsConnect.setDisable(true);
|
|
||||||
this.optionsEdit.setDisable(false);
|
|
||||||
this.optionsDelete.setDisable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setErrorMotd(String message) {
|
|
||||||
Text text = new Text(message);
|
|
||||||
text.setFill(Color.RED);
|
|
||||||
this.motdField.getChildren().setAll(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (this.server.isReadOnly()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.server.getConnections().forEach(PlayConnection::disconnect);
|
|
||||||
this.server.delete();
|
|
||||||
Log.info(String.format("Deleted server (name=\"%s\", address=\"%s\")", this.server.getName().getLegacyText(), this.server.getAddress()));
|
|
||||||
SERVER_LIST_VIEW.getItems().remove(this.server);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh() {
|
|
||||||
if (this.server.getLastPing() == null) {
|
|
||||||
// server was not pinged, don't even try, only costs memory and cpu
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.info(String.format("Refreshing server status (serverName=\"%s\", address=\"%s\")", this.server.getName().getLegacyText(), this.server.getAddress()));
|
|
||||||
resetCell();
|
|
||||||
this.server.ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clicked(MouseEvent e) {
|
|
||||||
switch (e.getButton()) {
|
|
||||||
case PRIMARY -> {
|
|
||||||
if (e.getClickCount() == 2) {
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case SECONDARY -> this.optionsMenu.fire();
|
|
||||||
case MIDDLE -> {
|
|
||||||
SERVER_LIST_VIEW.getSelectionModel().select(this.server);
|
|
||||||
editServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() {
|
|
||||||
if (!this.canConnect || this.server.getLastPing() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.server.isConnected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.root.getStyleClass().add("list-cell-connecting");
|
|
||||||
Minosoft.THREAD_POOL.execute(() -> {
|
|
||||||
Version version;
|
|
||||||
if (this.server.getDesiredVersionId() == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
|
||||||
version = this.server.getLastPing().getServerVersion();
|
|
||||||
} else {
|
|
||||||
version = Versions.getVersionById(this.server.getDesiredVersionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayConnection connection = new PlayConnection(this.server.getLastPing().getRealAddress(), Minosoft.getConfig().getConfig().getAccount().getEntries().get(Minosoft.getConfig().getConfig().getAccount().getSelected()), version);
|
|
||||||
this.server.addConnection(connection);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
this.optionsConnect.setDisable(true);
|
|
||||||
});
|
|
||||||
// ToDo: show progress dialog
|
|
||||||
|
|
||||||
connection.registerEvent(new CallbackEventInvoker<>(this::handleConnectionCallback));
|
|
||||||
connection.connect(new CountUpAndDownLatch(1));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void editServer() {
|
|
||||||
MainWindow.addOrEditServer(this.server);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleConnectionCallback(ConnectionStateChangeEvent event) {
|
|
||||||
PlayConnection connection = (PlayConnection) event.getConnection();
|
|
||||||
if (!this.server.getConnections().contains(connection)) {
|
|
||||||
// the card got recycled
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-connecting");
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-connected");
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-disconnecting");
|
|
||||||
this.root.getStyleClass().removeAll("list-cell-failed");
|
|
||||||
this.root.getStyleClass().add(switch (connection.getConnectionState()) {
|
|
||||||
case CONNECTING, HANDSHAKING, LOGIN -> "list-cell-connecting";
|
|
||||||
case PLAY -> "list-cell-connected";
|
|
||||||
case DISCONNECTING -> "list-cell-disconnecting";
|
|
||||||
case FAILED, FAILED_NO_RETRY -> "list-cell-failed";
|
|
||||||
default -> "";
|
|
||||||
});
|
|
||||||
|
|
||||||
if (connection.isConnected()) {
|
|
||||||
this.optionsConnect.setDisable(Minosoft.getConfig().getConfig().getAccount().getSelected().equals(connection.getAccount().getId()));
|
|
||||||
this.optionsSessions.setDisable(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.server.isConnected()) {
|
|
||||||
this.optionsSessions.setDisable(false);
|
|
||||||
this.optionsConnect.setDisable(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.optionsConnect.setDisable(false);
|
|
||||||
this.optionsSessions.setDisable(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showInfo() {
|
|
||||||
JFXAlert<?> dialog = new JFXAlert<>();
|
|
||||||
dialog.setTitle("View server info: " + this.server.getName().getMessage());
|
|
||||||
GUITools.initializePane(dialog.getDialogPane());
|
|
||||||
|
|
||||||
JFXDialogLayout layout = new JFXDialogLayout();
|
|
||||||
|
|
||||||
JFXButton closeButton = new JFXButton(ButtonType.CLOSE.getText());
|
|
||||||
closeButton.setOnAction((actionEvent -> dialog.hide()));
|
|
||||||
closeButton.setButtonType(JFXButton.ButtonType.RAISED);
|
|
||||||
layout.setActions(closeButton);
|
|
||||||
|
|
||||||
GridPane grid = new GridPane();
|
|
||||||
grid.setHgap(10);
|
|
||||||
grid.setVgap(10);
|
|
||||||
|
|
||||||
TextFlow serverNameLabel = new TextFlow();
|
|
||||||
serverNameLabel.getChildren().setAll(this.server.getName().getJavaFXText());
|
|
||||||
Label serverAddressLabel = new Label(this.server.getAddress());
|
|
||||||
Label forcedVersionLabel = new Label();
|
|
||||||
|
|
||||||
if (this.server.getDesiredVersionId() == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
|
||||||
forcedVersionLabel.setText(Versions.AUTOMATIC_VERSION.getVersionName());
|
|
||||||
} else {
|
|
||||||
forcedVersionLabel.setText(Versions.getVersionById(this.server.getDesiredVersionId()).getVersionName());
|
|
||||||
}
|
|
||||||
|
|
||||||
int column = -1;
|
|
||||||
Label a = new Label(LocaleManager.translate(Strings.SERVER_NAME) + ":");
|
|
||||||
a.setWrapText(false);
|
|
||||||
grid.add(a, 0, ++column);
|
|
||||||
grid.add(serverNameLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_ADDRESS) + ":"), 0, ++column);
|
|
||||||
grid.add(serverAddressLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.FORCED_VERSION) + ":"), 0, ++column);
|
|
||||||
grid.add(forcedVersionLabel, 1, column);
|
|
||||||
|
|
||||||
if (this.server.getLastPing() != null) {
|
|
||||||
if (this.server.getLastPing().getLastException() != null) {
|
|
||||||
Label lastConnectionExceptionLabel = new Label(this.server.getLastPing().getLastException().toString());
|
|
||||||
lastConnectionExceptionLabel.setStyle("-fx-text-fill: red");
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_LAST_CONNECTION_EXCEPTION) + ":"), 0, ++column);
|
|
||||||
grid.add(lastConnectionExceptionLabel, 1, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.server.getLastPing().getLastPing() != null) {
|
|
||||||
ServerListPing lastPing = this.server.getLastPing().getLastPing();
|
|
||||||
Version serverVersion = Versions.getVersionByProtocolId(lastPing.getProtocolId());
|
|
||||||
String serverVersionString;
|
|
||||||
if (serverVersion == null) {
|
|
||||||
serverVersionString = LocaleManager.translate(Strings.SERVER_INFO_VERSION_UNKNOWN, lastPing.getProtocolId());
|
|
||||||
} else {
|
|
||||||
serverVersionString = serverVersion.getVersionName();
|
|
||||||
}
|
|
||||||
Label realServerAddressLabel = new Label(this.server.getLastPing().getAddress());
|
|
||||||
Label serverVersionLabel = new Label(serverVersionString);
|
|
||||||
Label serverBrandLabel = new Label(lastPing.getServerBrand());
|
|
||||||
Label playersOnlineMaxLabel = new Label(LocaleManager.translate(Strings.SERVER_INFO_SLOTS_PLAYERS_ONLINE, lastPing.getPlayerOnline(), lastPing.getMaxPlayers()));
|
|
||||||
TextFlow motdLabel = new TextFlow();
|
|
||||||
motdLabel.getChildren().setAll(lastPing.getMotd().getJavaFXText());
|
|
||||||
Label moddedBrandLabel = new Label(lastPing.getServerModInfo().getBrand());
|
|
||||||
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_REAL_SERVER_ADDRESS) + ":"), 0, ++column);
|
|
||||||
grid.add(realServerAddressLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.VERSION) + ":"), 0, ++column);
|
|
||||||
grid.add(serverVersionLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_SERVER_BRAND) + ":"), 0, ++column);
|
|
||||||
grid.add(serverBrandLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_PLAYERS_ONLINE) + ":"), 0, ++column);
|
|
||||||
grid.add(playersOnlineMaxLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_MESSAGE_OF_THE_DAY) + ":"), 0, ++column);
|
|
||||||
grid.add(motdLabel, 1, column);
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_SERVER_MODDED_BRAND) + ":"), 0, ++column);
|
|
||||||
grid.add(moddedBrandLabel, 1, column);
|
|
||||||
|
|
||||||
if (lastPing.getServerModInfo() instanceof ForgeModInfo forgeModInfo) {
|
|
||||||
Label moddedModsLabel = new Label(forgeModInfo.getModList().toString());
|
|
||||||
moddedModsLabel.setWrapText(true);
|
|
||||||
|
|
||||||
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_SERVER_MODDED_MOD_LIST) + ":"), 0, ++column);
|
|
||||||
grid.add(moddedModsLabel, 1, column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ToDo: size probably
|
|
||||||
layout.setBody(grid);
|
|
||||||
dialog.setContent(layout);
|
|
||||||
dialog.showAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void manageSessions() {
|
|
||||||
try {
|
|
||||||
SessionsWindow sessionsWindow = GUITools.showPane(new ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "layout/sessions.fxml"), Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.SESSIONS_DIALOG_TITLE, this.server.getName().getMessage()));
|
|
||||||
sessionsWindow.setServer(this.server);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
358
src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.kt
Normal file
358
src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.kt
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* 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.Minosoft
|
||||||
|
import de.bixilon.minosoft.data.locale.LocaleManager
|
||||||
|
import de.bixilon.minosoft.data.locale.Strings
|
||||||
|
import de.bixilon.minosoft.data.mappings.ResourceLocation
|
||||||
|
import de.bixilon.minosoft.data.mappings.versions.Version
|
||||||
|
import de.bixilon.minosoft.data.mappings.versions.Versions
|
||||||
|
import de.bixilon.minosoft.data.player.tab.PingBars
|
||||||
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
|
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
|
||||||
|
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent
|
||||||
|
import de.bixilon.minosoft.modding.event.events.ServerListPongEvent
|
||||||
|
import de.bixilon.minosoft.modding.event.events.ServerListStatusArriveEvent
|
||||||
|
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||||
|
import de.bixilon.minosoft.protocol.ping.ServerModInfo
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
|
import de.bixilon.minosoft.util.CountUpAndDownLatch
|
||||||
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
|
import javafx.application.Platform
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.Initializable
|
||||||
|
import javafx.scene.control.*
|
||||||
|
import javafx.scene.image.ImageView
|
||||||
|
import javafx.scene.input.MouseButton
|
||||||
|
import javafx.scene.input.MouseEvent
|
||||||
|
import javafx.scene.layout.GridPane
|
||||||
|
import javafx.scene.layout.HBox
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
import javafx.scene.text.Text
|
||||||
|
import javafx.scene.text.TextFlow
|
||||||
|
import javafx.stage.Modality
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class ServerListCell : ListCell<Server?>(), Initializable {
|
||||||
|
lateinit var hBox: HBox
|
||||||
|
lateinit var root: GridPane
|
||||||
|
lateinit var faviconField: ImageView
|
||||||
|
lateinit var nameField: TextFlow
|
||||||
|
lateinit var motdField: TextFlow
|
||||||
|
lateinit var versionField: Label
|
||||||
|
lateinit var playersField: Label
|
||||||
|
lateinit var brandField: Label
|
||||||
|
lateinit var optionsConnect: MenuItem
|
||||||
|
lateinit var optionsShowInfo: MenuItem
|
||||||
|
lateinit var optionsEdit: MenuItem
|
||||||
|
lateinit var optionsRefresh: MenuItem
|
||||||
|
lateinit var optionsSessions: MenuItem
|
||||||
|
lateinit var optionsDelete: MenuItem
|
||||||
|
lateinit var optionsMenu: MenuButton
|
||||||
|
lateinit var pingField: Label
|
||||||
|
private var server: Server? = null
|
||||||
|
private var connectable = true
|
||||||
|
|
||||||
|
private var _name: ChatComponent = ChatComponent.valueOf(raw = "")
|
||||||
|
|
||||||
|
var name: ChatComponent
|
||||||
|
get() = _name
|
||||||
|
set(value) {
|
||||||
|
_name = value
|
||||||
|
nameField.children.setAll(name.javaFXText)
|
||||||
|
for (node in nameField.children) {
|
||||||
|
node.style = "-fx-font-size: 15pt ;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initialize(url: URL, rb: ResourceBundle?) {
|
||||||
|
updateSelected(false)
|
||||||
|
graphic = this.root
|
||||||
|
|
||||||
|
// change locale
|
||||||
|
optionsConnect.text = LocaleManager.translate(Strings.SERVER_ACTION_CONNECT)
|
||||||
|
optionsShowInfo.text = LocaleManager.translate(Strings.SERVER_ACTION_SHOW_INFO)
|
||||||
|
optionsEdit.text = LocaleManager.translate(Strings.SERVER_ACTION_EDIT)
|
||||||
|
optionsRefresh.text = LocaleManager.translate(Strings.SERVER_ACTION_REFRESH)
|
||||||
|
optionsSessions.text = LocaleManager.translate(Strings.SERVER_ACTION_SESSIONS)
|
||||||
|
optionsDelete.text = LocaleManager.translate(Strings.SERVER_ACTION_DELETE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateItem(server: Server?, empty: Boolean) {
|
||||||
|
super.updateItem(server, empty)
|
||||||
|
|
||||||
|
this.root.isVisible = server != null || !empty
|
||||||
|
this.hBox.isVisible = server != null || !empty
|
||||||
|
|
||||||
|
if (empty || server == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCell()
|
||||||
|
|
||||||
|
server.cell = this
|
||||||
|
|
||||||
|
this.server = server
|
||||||
|
|
||||||
|
name = server.name
|
||||||
|
|
||||||
|
faviconField.image = GUITools.getImage(server.favicon) ?: GUITools.MINOSOFT_LOGO
|
||||||
|
|
||||||
|
if (server.isConnected) {
|
||||||
|
root.styleClass.add("list-cell-connected")
|
||||||
|
optionsSessions.isDisable = false
|
||||||
|
} else {
|
||||||
|
optionsSessions.isDisable = true
|
||||||
|
}
|
||||||
|
if (server.lastPing == null) {
|
||||||
|
server.ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
server.lastPing.registerEvent(CallbackEventInvoker.of<ServerListStatusArriveEvent> {
|
||||||
|
Platform.runLater {
|
||||||
|
val ping = it.serverListPing
|
||||||
|
|
||||||
|
if (this.server != server) {
|
||||||
|
// this cell does not belong here anymore
|
||||||
|
return@runLater
|
||||||
|
}
|
||||||
|
resetCell()
|
||||||
|
if (server.isConnected) {
|
||||||
|
root.styleClass.add("list-cell-connected")
|
||||||
|
}
|
||||||
|
if (ping == null) {
|
||||||
|
this.playersField.text = ""
|
||||||
|
this.versionField.text = LocaleManager.translate(Strings.OFFLINE)
|
||||||
|
this.versionField.styleClass.add("version-error")
|
||||||
|
setErrorMotd(server.lastPing.lastException.toString())
|
||||||
|
this.optionsConnect.isDisable = true
|
||||||
|
this.connectable = false
|
||||||
|
return@runLater
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playersField.text = LocaleManager.translate(Strings.SERVER_INFO_SLOTS_PLAYERS_ONLINE, ping.playerOnline, ping.maxPlayers)
|
||||||
|
|
||||||
|
val serverVersion: Version
|
||||||
|
|
||||||
|
if (server.desiredVersionId == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
||||||
|
serverVersion = Versions.getVersionByProtocolId(ping.protocolId)
|
||||||
|
} else {
|
||||||
|
serverVersion = Versions.getVersionById(server.desiredVersionId)
|
||||||
|
versionField.style = "-fx-text-fill: -secondary-light-light-color;"
|
||||||
|
}
|
||||||
|
if (serverVersion == null) {
|
||||||
|
versionField.text = ping.serverBrand
|
||||||
|
versionField.style = "-fx-text-fill: red;"
|
||||||
|
optionsConnect.isDisable = true
|
||||||
|
connectable = false
|
||||||
|
} else {
|
||||||
|
versionField.text = serverVersion.versionName
|
||||||
|
optionsConnect.isDisable = false
|
||||||
|
connectable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ping.getServerModInfo<ServerModInfo>().let { modInfo ->
|
||||||
|
brandField.text = modInfo.brand
|
||||||
|
brandField.tooltip = Tooltip(modInfo.info)
|
||||||
|
}
|
||||||
|
motdField.children.setAll(ping.motd.javaFXText)
|
||||||
|
|
||||||
|
ping.favicon?.let {
|
||||||
|
this.faviconField.image = GUITools.getImage(ping.favicon)
|
||||||
|
if (!Arrays.equals(ping.favicon, server.favicon)) {
|
||||||
|
server.favicon = ping.favicon
|
||||||
|
server.saveToConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.isTemporary) {
|
||||||
|
this.optionsEdit.isDisable = true
|
||||||
|
this.optionsDelete.isDisable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
server.lastPing.lastException?.let { exception ->
|
||||||
|
// connection failed because of an error in minosoft, but ping was okay
|
||||||
|
this.versionField.style = "-fx-text-fill: red;"
|
||||||
|
this.optionsConnect.isDisable = true
|
||||||
|
this.connectable = false
|
||||||
|
setErrorMotd(String.format("%s: %s", exception::class.java.canonicalName, exception.message))
|
||||||
|
}
|
||||||
|
|
||||||
|
server.lastPing.registerEvent(CallbackEventInvoker.of<ServerListPongEvent> {
|
||||||
|
Platform.runLater {
|
||||||
|
this.pingField.text = "${it.latency}ms"
|
||||||
|
when (PingBars.byPing(it.latency)) {
|
||||||
|
PingBars.BARS_5 -> this.pingField.styleClass.add("ping-5-bars")
|
||||||
|
PingBars.BARS_4 -> this.pingField.styleClass.add("ping-4-bars")
|
||||||
|
PingBars.BARS_3 -> this.pingField.styleClass.add("ping-3-bars")
|
||||||
|
PingBars.BARS_2 -> this.pingField.styleClass.add("ping-2-bars")
|
||||||
|
PingBars.BARS_1 -> this.pingField.styleClass.add("ping-1-bars")
|
||||||
|
PingBars.NO_CONNECTION -> this.pingField.styleClass.add("ping-no-connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetCell() {
|
||||||
|
// clear all cells
|
||||||
|
style = null
|
||||||
|
this.root.styleClass.removeAll("list-cell-connected")
|
||||||
|
this.root.styleClass.removeAll("list-cell-connecting")
|
||||||
|
motdField.children.clear()
|
||||||
|
brandField.text = ""
|
||||||
|
brandField.tooltip = null
|
||||||
|
motdField.style = null
|
||||||
|
versionField.text = LocaleManager.translate(Strings.CONNECTING)
|
||||||
|
versionField.styleClass.removeAll("version-error")
|
||||||
|
versionField.style = null
|
||||||
|
playersField.text = ""
|
||||||
|
pingField.text = ""
|
||||||
|
pingField.styleClass.removeIf { s: String -> s.startsWith("ping") }
|
||||||
|
optionsConnect.isDisable = true
|
||||||
|
optionsEdit.isDisable = false
|
||||||
|
optionsDelete.isDisable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setErrorMotd(message: String) {
|
||||||
|
val text = Text(message)
|
||||||
|
text.fill = Color.RED
|
||||||
|
motdField.children.setAll(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connect() {
|
||||||
|
val server = this.server!!
|
||||||
|
if (!this.connectable || server.lastPing == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (server.isConnected) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.root.styleClass.add("list-cell-connecting")
|
||||||
|
Minosoft.THREAD_POOL.execute {
|
||||||
|
val version: Version = if (server.desiredVersionId == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
||||||
|
server.lastPing.serverVersion!!
|
||||||
|
} else {
|
||||||
|
Versions.getVersionById(server.desiredVersionId)
|
||||||
|
}
|
||||||
|
val connection = PlayConnection(server.lastPing.realAddress, Minosoft.getConfig().config.account.entries[Minosoft.getConfig().config.account.selected]!!, version)
|
||||||
|
server.addConnection(connection)
|
||||||
|
Platform.runLater { optionsConnect.isDisable = true }
|
||||||
|
// ToDo: show progress dialog
|
||||||
|
connection.registerEvent(CallbackEventInvoker.of<ConnectionStateChangeEvent> {
|
||||||
|
if (!server.connections.contains(connection)) {
|
||||||
|
// the card got recycled
|
||||||
|
return@of
|
||||||
|
}
|
||||||
|
Platform.runLater {
|
||||||
|
this.root.styleClass.removeAll("list-cell-connecting")
|
||||||
|
this.root.styleClass.removeAll("list-cell-connected")
|
||||||
|
this.root.styleClass.removeAll("list-cell-disconnecting")
|
||||||
|
this.root.styleClass.removeAll("list-cell-failed")
|
||||||
|
this.root.styleClass.add(when (connection.connectionState) {
|
||||||
|
ConnectionStates.CONNECTING, ConnectionStates.HANDSHAKING, ConnectionStates.LOGIN -> "list-cell-connecting"
|
||||||
|
ConnectionStates.PLAY -> "list-cell-connected"
|
||||||
|
ConnectionStates.DISCONNECTING -> "list-cell-disconnecting"
|
||||||
|
ConnectionStates.FAILED, ConnectionStates.FAILED_NO_RETRY -> "list-cell-failed"
|
||||||
|
else -> ""
|
||||||
|
})
|
||||||
|
if (connection.isConnected) {
|
||||||
|
optionsConnect.isDisable = Minosoft.getConfig().config.account.selected == connection.account.id
|
||||||
|
optionsSessions.isDisable = false
|
||||||
|
return@runLater
|
||||||
|
}
|
||||||
|
if (server.isConnected) {
|
||||||
|
optionsSessions.isDisable = false
|
||||||
|
optionsConnect.isDisable = false
|
||||||
|
return@runLater
|
||||||
|
}
|
||||||
|
optionsConnect.isDisable = false
|
||||||
|
optionsSessions.isDisable = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
connection.connect(CountUpAndDownLatch(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showInfo() {
|
||||||
|
TODO("Not implemented yet!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clicked(event: MouseEvent) {
|
||||||
|
when (event.button) {
|
||||||
|
MouseButton.PRIMARY -> {
|
||||||
|
if (event.clickCount == 2) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MouseButton.SECONDARY -> this.optionsMenu.fire()
|
||||||
|
MouseButton.MIDDLE -> {
|
||||||
|
SERVER_LIST_VIEW.selectionModel.select(this.server)
|
||||||
|
editServer()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete() {
|
||||||
|
val server = this.server!!
|
||||||
|
if (server.isTemporary) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (connection in server.connections) {
|
||||||
|
connection.disconnect()
|
||||||
|
}
|
||||||
|
server.delete()
|
||||||
|
Log.info("Deleted server (name=\"${server.name}\", address=\"${server.address}\")")
|
||||||
|
SERVER_LIST_VIEW.items.remove(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
val server = this.server!!
|
||||||
|
if (server.lastPing == null) {
|
||||||
|
// server was not pinged, don't even try, only costs memory and cpu
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.info("Refreshing server status (serverName=\"${server.name}\", address=\"${server.address}\")")
|
||||||
|
resetCell()
|
||||||
|
server.ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun editServer() {
|
||||||
|
MainWindow.addOrEditServer(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun manageSessions() {
|
||||||
|
val sessionsWindow = GUITools.showPane<SessionsWindow>(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "layout/sessions.fxml"), Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.SESSIONS_DIALOG_TITLE, server!!.name.message))
|
||||||
|
sessionsWindow.setServer(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val SERVER_LIST_VIEW = ListView<Server>()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newInstance(): ServerListCell {
|
||||||
|
val loader = FXMLLoader(Minosoft.MINOSOFT_ASSETS_MANAGER.getAssetURL(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "layout/cells/server.fxml")))
|
||||||
|
|
||||||
|
loader.load<Any>()
|
||||||
|
return loader.getController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.Minosoft;
|
|
||||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
|
||||||
import de.bixilon.minosoft.data.locale.Strings;
|
|
||||||
import de.bixilon.minosoft.data.mappings.ResourceLocation;
|
|
||||||
import de.bixilon.minosoft.modding.event.CallbackEventInvoker;
|
|
||||||
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent;
|
|
||||||
import de.bixilon.minosoft.protocol.network.connection.PlayConnection;
|
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
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.control.MenuItem;
|
|
||||||
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<PlayConnection> implements Initializable {
|
|
||||||
public static final ListView<PlayConnection> CONNECTION_LIST_VIEW = new ListView<>();
|
|
||||||
|
|
||||||
public Label account;
|
|
||||||
public Label connectionId;
|
|
||||||
public MenuItem optionsDisconnect;
|
|
||||||
public AnchorPane root;
|
|
||||||
|
|
||||||
private PlayConnection connection;
|
|
||||||
|
|
||||||
public static SessionListCell newInstance() {
|
|
||||||
FXMLLoader loader = new FXMLLoader(Minosoft.MINOSOFT_ASSETS_MANAGER.getAssetURL(new ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "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(this.root);
|
|
||||||
|
|
||||||
this.optionsDisconnect.setText(LocaleManager.translate(Strings.SESSIONS_ACTION_DISCONNECT));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnchorPane getRoot() {
|
|
||||||
return this.root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateItem(PlayConnection connection, boolean empty) {
|
|
||||||
super.updateItem(connection, empty);
|
|
||||||
|
|
||||||
this.root.setVisible(!empty);
|
|
||||||
if (empty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection.equals(this.connection)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setStyle(null);
|
|
||||||
this.connection = connection;
|
|
||||||
connection.registerEvent(new CallbackEventInvoker<>(this::handleConnectionCallback));
|
|
||||||
this.connectionId.setText(String.format("#%d", connection.getConnectionId()));
|
|
||||||
this.account.setText(connection.getAccount().getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleConnectionCallback(ConnectionStateChangeEvent event) {
|
|
||||||
PlayConnection connection = (PlayConnection) event.getConnection();
|
|
||||||
if (this.connection != connection) {
|
|
||||||
// the card got recycled
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!connection.isConnected()) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
CONNECTION_LIST_VIEW.getItems().remove(connection);
|
|
||||||
if (CONNECTION_LIST_VIEW.getItems().isEmpty()) {
|
|
||||||
((Stage) this.root.getScene().getWindow()).close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
setStyle("-fx-background-color: indianred");
|
|
||||||
this.connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
101
src/main/java/de/bixilon/minosoft/gui/main/SessionListCell.kt
Normal file
101
src/main/java/de/bixilon/minosoft/gui/main/SessionListCell.kt
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.Minosoft
|
||||||
|
import de.bixilon.minosoft.data.locale.LocaleManager
|
||||||
|
import de.bixilon.minosoft.data.locale.Strings
|
||||||
|
import de.bixilon.minosoft.data.mappings.ResourceLocation
|
||||||
|
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
|
||||||
|
import de.bixilon.minosoft.modding.event.events.ConnectionStateChangeEvent
|
||||||
|
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
|
import javafx.application.Platform
|
||||||
|
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.control.MenuItem
|
||||||
|
import javafx.scene.layout.AnchorPane
|
||||||
|
import javafx.stage.Stage
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SessionListCell : ListCell<PlayConnection?>(), Initializable {
|
||||||
|
lateinit var account: Label
|
||||||
|
lateinit var connectionId: Label
|
||||||
|
lateinit var optionsDisconnect: MenuItem
|
||||||
|
lateinit var root: AnchorPane
|
||||||
|
private var connection: PlayConnection? = null
|
||||||
|
|
||||||
|
override fun initialize(url: URL, rb: ResourceBundle?) {
|
||||||
|
updateSelected(false)
|
||||||
|
graphic = this.root
|
||||||
|
optionsDisconnect.text = LocaleManager.translate(Strings.SESSIONS_ACTION_DISCONNECT)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateItem(connection: PlayConnection?, empty: Boolean) {
|
||||||
|
super.updateItem(connection, empty)
|
||||||
|
this.root.isVisible = !empty
|
||||||
|
if (empty) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (connection == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (connection == this.connection) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
style = null
|
||||||
|
this.connection = connection
|
||||||
|
connection.registerEvent(CallbackEventInvoker.of<ConnectionStateChangeEvent> {
|
||||||
|
handleConnectionCallback(it)
|
||||||
|
})
|
||||||
|
connectionId.text = String.format("#%d", connection.connectionId)
|
||||||
|
account.text = connection.account.username
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleConnectionCallback(event: ConnectionStateChangeEvent) {
|
||||||
|
val connection = event.connection as PlayConnection
|
||||||
|
if (this.connection != connection) {
|
||||||
|
// the card got recycled
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!connection.isConnected) {
|
||||||
|
Platform.runLater {
|
||||||
|
CONNECTION_LIST_VIEW.items.remove(connection)
|
||||||
|
if (CONNECTION_LIST_VIEW.items.isEmpty()) {
|
||||||
|
(this.root.scene.window as Stage).close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disconnect() {
|
||||||
|
style = "-fx-background-color: indianred"
|
||||||
|
connection!!.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val CONNECTION_LIST_VIEW = ListView<PlayConnection>()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newInstance(): SessionListCell? {
|
||||||
|
val loader = FXMLLoader(Minosoft.MINOSOFT_ASSETS_MANAGER.getAssetURL(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "layout/cells/session.fxml")))
|
||||||
|
loader.load<Any>()
|
||||||
|
return loader.getController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -129,17 +129,17 @@ class RenderWindow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
connection.registerEvent(CallbackEventInvoker<ConnectionStateChangeEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<ConnectionStateChangeEvent> {
|
||||||
if (it.connection.isDisconnected) {
|
if (it.connection.isDisconnected) {
|
||||||
renderQueue.add {
|
renderQueue.add {
|
||||||
glfwSetWindowShouldClose(windowId, true)
|
glfwSetWindowShouldClose(windowId, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
connection.registerEvent(CallbackEventInvoker<PacketReceiveEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<PacketReceiveEvent> {
|
||||||
val packet = it.packet
|
val packet = it.packet
|
||||||
if (packet !is PacketPlayerPositionAndRotation) {
|
if (packet !is PacketPlayerPositionAndRotation) {
|
||||||
return@CallbackEventInvoker
|
return@of
|
||||||
}
|
}
|
||||||
if (latch.count > 0) {
|
if (latch.count > 0) {
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
|
@ -127,19 +127,19 @@ class WorldRenderer(
|
|||||||
prepareWorld(world)
|
prepareWorld(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.registerEvent(CallbackEventInvoker<ChunkUnloadEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<ChunkUnloadEvent> {
|
||||||
unloadChunk(it.chunkPosition)
|
unloadChunk(it.chunkPosition)
|
||||||
})
|
})
|
||||||
|
|
||||||
connection.registerEvent(CallbackEventInvoker<ChunkDataChangeEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<ChunkDataChangeEvent> {
|
||||||
prepareChunk(it.chunkPosition)
|
prepareChunk(it.chunkPosition)
|
||||||
})
|
})
|
||||||
|
|
||||||
connection.registerEvent(CallbackEventInvoker<BlockChangeEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<BlockChangeEvent> {
|
||||||
prepareChunkSection(it.blockPosition.chunkPosition, it.blockPosition.sectionHeight)
|
prepareChunkSection(it.blockPosition.chunkPosition, it.blockPosition.sectionHeight)
|
||||||
})
|
})
|
||||||
|
|
||||||
connection.registerEvent(CallbackEventInvoker<MultiBlockChangeEvent> {
|
connection.registerEvent(CallbackEventInvoker.of<MultiBlockChangeEvent> {
|
||||||
val sectionHeights: MutableSet<Int> = mutableSetOf()
|
val sectionHeights: MutableSet<Int> = mutableSetOf()
|
||||||
for ((key) in it.blocks) {
|
for ((key) in it.blocks) {
|
||||||
sectionHeights.add(key.sectionHeight)
|
sectionHeights.add(key.sectionHeight)
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.modding.event;
|
|
||||||
|
|
||||||
import de.bixilon.minosoft.modding.event.events.Event;
|
|
||||||
import de.bixilon.minosoft.modding.loading.Priorities;
|
|
||||||
|
|
||||||
public class CallbackEventInvoker<V extends Event> extends EventInvoker {
|
|
||||||
private final InvokerCallback<V> callback;
|
|
||||||
|
|
||||||
public CallbackEventInvoker(boolean ignoreCancelled, InvokerCallback<V> callback) {
|
|
||||||
super(ignoreCancelled, Priorities.NORMAL, null);
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if you need instant fireing support
|
|
||||||
public CallbackEventInvoker(InvokerCallback<V> callback) {
|
|
||||||
this(false, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void invoke(Event event) {
|
|
||||||
try {
|
|
||||||
this.callback.handle((V) event);
|
|
||||||
} catch (ClassCastException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends Event> getEventType() {
|
|
||||||
return Event.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface InvokerCallback<V> {
|
|
||||||
void handle(V event);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.modding.event
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.modding.event.events.Event
|
||||||
|
import de.bixilon.minosoft.modding.loading.Priorities
|
||||||
|
|
||||||
|
class CallbackEventInvoker<E : Event?> private constructor(
|
||||||
|
ignoreCancelled: Boolean,
|
||||||
|
private val callback: (event: E) -> Unit,
|
||||||
|
private val eventType: Class<out Event>,
|
||||||
|
) : EventInvoker(ignoreCancelled, Priorities.NORMAL, null) {
|
||||||
|
|
||||||
|
override fun invoke(event: Event) {
|
||||||
|
callback.invoke(event as E)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEventType(): Class<out Event> {
|
||||||
|
return eventType
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmOverloads
|
||||||
|
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||||
|
inline fun <reified E : Event> of(ignoreCancelled: Boolean = false, noinline callback: (event: E) -> Unit): CallbackEventInvoker<E> {
|
||||||
|
return CallbackEventInvoker(
|
||||||
|
ignoreCancelled = ignoreCancelled,
|
||||||
|
callback = callback,
|
||||||
|
eventType = E::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -56,6 +56,9 @@ abstract class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (eventInvoker in eventListeners) {
|
for (eventInvoker in eventListeners) {
|
||||||
|
if (!eventInvoker.eventType.isAssignableFrom(connectionEvent::class.java)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
eventInvoker.invoke(connectionEvent)
|
eventInvoker.invoke(connectionEvent)
|
||||||
}
|
}
|
||||||
if (connectionEvent is CancelableEvent) {
|
if (connectionEvent is CancelableEvent) {
|
||||||
|
@ -149,10 +149,11 @@ class StatusConnection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun registerEvent(method: EventInvoker) {
|
override fun registerEvent(method: EventInvoker) {
|
||||||
if (method.eventType.isAssignableFrom(ServerListStatusArriveEvent::class.java) && wasPingDone()) {
|
val wasPingDone = wasPingDone()
|
||||||
|
if (method.eventType.isAssignableFrom(ServerListStatusArriveEvent::class.java) && wasPingDone) {
|
||||||
// ping done
|
// ping done
|
||||||
method.invoke(ServerListStatusArriveEvent(this, this.lastPing))
|
method.invoke(ServerListStatusArriveEvent(this, this.lastPing))
|
||||||
} else if (method.eventType.isAssignableFrom(ServerListPongEvent::class.java) && wasPingDone() && this.pong != null) {
|
} else if (method.eventType.isAssignableFrom(ServerListPongEvent::class.java) && wasPingDone && this.pong != null) {
|
||||||
method.invoke(this.pong)
|
method.invoke(this.pong)
|
||||||
} else {
|
} else {
|
||||||
super.registerEvent(method)
|
super.registerEvent(method)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user