diff --git a/.idea/misc.xml b/.idea/misc.xml index a2cda41..078a25f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + - + \ No newline at end of file diff --git a/src/main/java/it/polimi/ingsw/gc14/View/GUI/LoginFXMLController.java b/src/main/java/it/polimi/ingsw/gc14/View/GUI/LoginFXMLController.java index 502059a..282a20c 100644 --- a/src/main/java/it/polimi/ingsw/gc14/View/GUI/LoginFXMLController.java +++ b/src/main/java/it/polimi/ingsw/gc14/View/GUI/LoginFXMLController.java @@ -3,111 +3,180 @@ package it.polimi.ingsw.gc14.View.GUI; import it.polimi.ingsw.gc14.Controller.ClientController; import it.polimi.ingsw.gc14.Network.RMI.Client.RMIClient; import it.polimi.ingsw.gc14.Network.TCP.Client.TCPClient; +import javafx.animation.*; import javafx.application.Platform; +import javafx.css.PseudoClass; import javafx.fxml.FXML; +import javafx.geometry.Rectangle2D; import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.stage.Screen; +import javafx.util.Duration; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.util.ArrayList; +import java.net.*; import java.util.Enumeration; -import java.util.List; public class LoginFXMLController { + @FXML private ImageView backgroundImage; @FXML private TextField campoNome; @FXML private TextField campoNumPlayers; @FXML private TextField campoIP; - @FXML private RadioButton btnRMI; - @FXML private RadioButton btnTCP; @FXML private Button btnAccedi; @FXML private Label labelErrore; - @FXML private ComboBox comboInterfaccia; + @FXML private VBox formPanel; + @FXML private StackPane protocolToggle; + @FXML private Region toggleThumb; + @FXML private Label labelRMI; + @FXML private Label labelTCP; + private boolean isRMI = true; private ClientController controller; + // Definiamo lo pseudo-stato custom per il CSS corrispondente a ":active-protocol" + private final PseudoClass activeProtocolPseudo = PseudoClass.getPseudoClass("active-protocol"); + public void setController(ClientController controller) { this.controller = controller; } @FXML public void initialize() { - ToggleGroup group = new ToggleGroup(); - btnRMI.setToggleGroup(group); - btnTCP.setToggleGroup(group); + // Background setup + Image img = new Image(getClass().getResourceAsStream("/GUIImages/Background.png")); + backgroundImage.setImage(img); + Rectangle2D screenBounds = Screen.getPrimary().getBounds(); + backgroundImage.setFitWidth(screenBounds.getWidth()); + backgroundImage.setFitHeight(screenBounds.getHeight()); - new Thread(() -> { - try { - List ips = new ArrayList<>(); - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface ni = interfaces.nextElement(); - if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue; - Enumeration addresses = ni.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress addr = addresses.nextElement(); - if (addr instanceof Inet4Address) - ips.add(ni.getDisplayName() + " -> " + addr.getHostAddress()); - } - } - Platform.runLater(() -> { - comboInterfaccia.getItems().addAll(ips); - if (!ips.isEmpty()) comboInterfaccia.getSelectionModel().selectFirst(); - }); - } catch (Exception e) { - Platform.runLater(() -> labelErrore.setText("Errore nel rilevare le interfacce.")); - } - }).start(); + // Protocol toggle configuration + protocolToggle.setOnMouseClicked(e -> switchProtocol()); + protocolToggle.setStyle(protocolToggle.getStyle() + " -fx-cursor: hand;"); + + // Inizializza lo stato grafico iniziale (RMI attivo) + labelRMI.pseudoClassStateChanged(activeProtocolPseudo, true); + labelTCP.pseudoClassStateChanged(activeProtocolPseudo, false); + + // Login Action btnAccedi.setOnAction(e -> onAccediClick()); } + private void switchProtocol() { + isRMI = !isRMI; + + TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb); + tt.setToX(isRMI ? 0 : 110); + tt.play(); + + // Modifica in modo sicuro lo stato senza distruggere i parametri di layout dei nodi + labelRMI.pseudoClassStateChanged(activeProtocolPseudo, isRMI); + labelTCP.pseudoClassStateChanged(activeProtocolPseudo, !isRMI); + } + @FXML private void onAccediClick() { String nome = campoNome.getText().trim(); - controller.setMyUsername(nome); String ip = campoIP.getText().trim(); + + if (nome.isEmpty()) { + showError("Please select a name."); + return; + } + int numPlayers; try { numPlayers = Integer.parseInt(campoNumPlayers.getText().trim()); } catch (NumberFormatException e) { - labelErrore.setText("Numero giocatori non valido."); + showError("Invalid number of players."); return; } - if (nome.isEmpty() || ip.isEmpty()) { - labelErrore.setText("Compila tutti i campi."); - return; - } - System.setProperty("java.rmi.server.hostname", ip); + controller.setMyUsername(nome); - int networkType = btnRMI.isSelected() ? 0 : 1; - String interfaccia = getInterfaccia(); - - // Stesso thread di rete che era in ClientLauncher new Thread(() -> { - if (networkType == 0) { - RMIClient client = new RMIClient(controller, ip, 1099, interfaccia); - if (client.connect(nome, numPlayers)) { - controller.setClient(client); - } else { - Platform.runLater(() -> labelErrore.setText("Connessione RMI fallita.")); - } - } else { - TCPClient client = new TCPClient(controller, ip, 8080, 8081); - if (client.connect(nome, numPlayers)) { - controller.setClient(client); - } else { - Platform.runLater(() -> labelErrore.setText("Connessione TCP fallita.")); - } + try { + String localInterface = resolveLocalInterface(ip); + System.setProperty("java.rmi.server.hostname", localInterface); + connect(nome, ip, numPlayers, localInterface); + } catch (Exception ex) { + Platform.runLater(() -> showError("Network error: " + ex.getMessage())); } }).start(); - } - private String getInterfaccia() { - String selected = comboInterfaccia.getValue(); - if (selected == null) return ""; - return selected.contains("->") ? selected.split("->")[1].trim() : selected; + private String resolveLocalInterface(String serverIp) throws Exception { + if (serverIp.equalsIgnoreCase("localhost") || serverIp.startsWith("127.") || serverIp.isEmpty()) { + return "127.0.0.1"; + } + + InetAddress serverAddr = InetAddress.getByName(serverIp); + Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); + while (ifaces.hasMoreElements()) { + NetworkInterface ni = ifaces.nextElement(); + if (!ni.isUp() || ni.isLoopback()) continue; + + for (InterfaceAddress ia : ni.getInterfaceAddresses()) { + InetAddress localAddr = ia.getAddress(); + if (!(localAddr instanceof Inet4Address)) continue; + + int prefix = ia.getNetworkPrefixLength(); + if (sameSubnet(localAddr, serverAddr, prefix)) { + return localAddr.getHostAddress(); + } + } + } + + try (DatagramSocket socket = new DatagramSocket()) { + socket.connect(serverAddr, 9); + return socket.getLocalAddress().getHostAddress(); + } + } + + private boolean sameSubnet(InetAddress a, InetAddress b, int prefix) { + if (prefix < 0 || prefix > 32) return false; + int maskBits = prefix == 0 ? 0 : (0xFFFFFFFF << (32 - prefix)); + int addrA = toInt(a.getAddress()) & maskBits; + int addrB = toInt(b.getAddress()) & maskBits; + return addrA == addrB; + } + + private int toInt(byte[] bytes) { + return ((bytes[0] & 0xFF) << 24) | + ((bytes[1] & 0xFF) << 16) | + ((bytes[2] & 0xFF) << 8) | + (bytes[3] & 0xFF); + } + + private void connect(String nome, String ip, int numPlayers, String localInterface) { + if (isRMI) { + RMIClient client = new RMIClient(controller, ip, 1099, localInterface); + if (client.connect(nome, numPlayers)) { + controller.setClient(client); + Platform.runLater(() -> showSuccess("Connected! Waiting for other players…")); + } else { + Platform.runLater(() -> showError("RMI connection failed.")); + } + } else { + TCPClient client = new TCPClient(controller, ip, 8080, 8081); + if (client.connect(nome, numPlayers)) { + controller.setClient(client); + Platform.runLater(() -> showSuccess("Connected! Waiting for other players…")); + } else { + Platform.runLater(() -> showError("TCP connection failed.")); + } + } + } + + private void showError(String msg) { + labelErrore.setTextFill(Color.web("#e05050")); + labelErrore.setText(msg); + } + + private void showSuccess(String msg) { + labelErrore.setTextFill(Color.web("#6fcf8a")); + labelErrore.setText(msg); } } \ No newline at end of file diff --git a/src/main/java/it/polimi/ingsw/gc14/View/GUI/MainFXMLController.java b/src/main/java/it/polimi/ingsw/gc14/View/GUI/MainFXMLController.java index 4e554c4..59ca329 100644 --- a/src/main/java/it/polimi/ingsw/gc14/View/GUI/MainFXMLController.java +++ b/src/main/java/it/polimi/ingsw/gc14/View/GUI/MainFXMLController.java @@ -471,16 +471,15 @@ public class MainFXMLController { StackPane card = createOrder(Integer.toString(numPlayers)); Map slotPositions = Map.of( - 2, new double[]{0.229 ,0.413}, - 3, new double[]{0.183,0.367, 0.548}, - 4, new double[]{0.142,0.316, 0.503, 0.690}, - 5, new double[]{0.066, 0.251, 0.433, 0.617,0.802} + 2, new double[]{0.229, 0.413}, + 3, new double[]{0.183, 0.367, 0.548}, + 4, new double[]{0.142, 0.316, 0.503, 0.690}, + 5, new double[]{0.066, 0.251, 0.433, 0.617, 0.802} ); double[] positions = slotPositions.get(numPlayers); if (positions == null) return; - Pane overlay = new Pane(); overlay.setPickOnBounds(false); overlay.setMouseTransparent(true); @@ -488,43 +487,42 @@ public class MainFXMLController { for (int i = 0; i < controller.miniModel.orderLogicCard.playerList.size(); i++) { OrderPlayer op = controller.miniModel.orderLogicCard.playerList.get(i); - // colore dal totem del giocatore - Color color = totemColor(op.player.totem); + // Carica immagine totem come in createSlot + ImageView totem = new ImageView(loadImage( + "/GUIImages/Totems/totem_" + op.player.totem.toString().toLowerCase(Locale.ROOT) + ".png" + )); + totem.setPreserveRatio(true); - Rectangle rect = new Rectangle(48, 26, color); - rect.setArcWidth(4); - rect.setArcHeight(4); if (controller.miniModel.slotPlayerMap.containsValue(op.player)) { - rect.setVisible(false); + totem.setVisible(false); } final double yRatio = positions[i]; + overlay.prefHeightProperty().bind(card.heightProperty()); overlay.prefWidthProperty().bind(card.widthProperty()); - // posizione X fissa, Y proporzionale + + // Scala altezza totem proporzionalmente come in createSlot (0.127 della card) + totem.fitHeightProperty().bind(card.heightProperty().multiply(0.323)); + + // Posizione Y proporzionale, X fissa card.heightProperty().addListener((obs, ov, nv) -> { - rect.setY(nv.doubleValue() * yRatio); - rect.setHeight(0.127*nv.doubleValue()); + totem.setLayoutY(nv.doubleValue() * (yRatio-0.196)); }); card.widthProperty().addListener((obs, ov, nv) -> { - rect.setX(nv.doubleValue() * 0.323); - rect.setWidth(0.375*nv.doubleValue()); + totem.setLayoutX(nv.doubleValue() * 0.364); }); - if (card.getHeight() > 0 && card.getWidth() > 0) { - rect.setY(card.getHeight() * yRatio); - rect.setHeight(0.127*yRatio); - rect.setX(card.getHeight() * 0.323); - rect.setWidth(0.375*card.getHeight()); - } + // Inizializzazione immediata se già dimensionato + if (card.getHeight() > 0) totem.setLayoutY(card.getHeight() *(yRatio-0.196)); + if (card.getWidth() > 0) totem.setLayoutX(card.getWidth() * 0.364); - overlay.getChildren().add(rect); + overlay.getChildren().add(totem); } card.getChildren().add(overlay); board.getChildren().add(card); } - private Color totemColor(Totems totem) { return switch (totem) { case ORANGE -> Color.rgb(223, 76, 17); diff --git a/src/main/resources/GUIScene/login.fxml b/src/main/resources/GUIScene/login.fxml index c9219e4..ba313ff 100644 --- a/src/main/resources/GUIScene/login.fxml +++ b/src/main/resources/GUIScene/login.fxml @@ -1,31 +1,150 @@ - + + + - - + -