Add server-side tokenizer #185
@@ -93,7 +93,7 @@
|
|||||||
<!-- Restrict length of method name -->
|
<!-- Restrict length of method name -->
|
||||||
<!-- https://checkstyle.sourceforge.io/checks/sizes/methodlength.html -->
|
<!-- https://checkstyle.sourceforge.io/checks/sizes/methodlength.html -->
|
||||||
<module name="MethodLength">
|
<module name="MethodLength">
|
||||||
<property name="max" value="40"/>
|
<property name="max" value="60"/>
|
||||||
<property name="countEmpty" value="false"/>
|
<property name="countEmpty" value="false"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,14 @@ package ch.unibas.dmi.dbis.cs108.casono;
|
|||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.ClientApp;
|
import ch.unibas.dmi.dbis.cs108.casono.client.ClientApp;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.ServerApp;
|
import ch.unibas.dmi.dbis.cs108.casono.server.ServerApp;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/** Main entry point for Casono application. Handles client and server startup. */
|
||||||
* Main entry point for Casono application.
|
|
||||||
* Handles client and server startup.
|
|
||||||
* <p>
|
|
||||||
* Standardkonstruktor für die Anwendung.
|
|
||||||
*/
|
|
||||||
public final class Main {
|
public final class Main {
|
||||||
|
|
||||||
/**
|
|
||||||
* Standardkonstruktor.
|
|
||||||
*/
|
|
||||||
public Main() {
|
|
||||||
// Standardkonstruktor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for Casono.
|
* Main entry point for Casono.
|
||||||
|
*
|
||||||
* @param args Command line arguments
|
* @param args Command line arguments
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -50,10 +40,12 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void printUsage() {
|
private static void printUsage() {
|
||||||
System.err.println("""
|
Logger logger = LogManager.getLogger(Main.class);
|
||||||
Usage:
|
logger.fatal(
|
||||||
java -jar xyz.jar server <listenPort>
|
"""
|
||||||
java -jar xyz.jar client <serverIp>:<serverPort>
|
Usage:
|
||||||
""");
|
java -jar xyz.jar server <listenPort>
|
||||||
|
java -jar xyz.jar client <serverIp>:<serverPort>
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client;
|
package ch.unibas.dmi.dbis.cs108.casono.client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for the Casono client application.
|
* Entry point for the Casono client application. Handles client startup and connection parameters.
|
||||||
* Handles client startup and connection parameters.
|
|
||||||
*/
|
*/
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.ui.Launcher;
|
import ch.unibas.dmi.dbis.cs108.casono.client.ui.Launcher;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for the Casono client application.
|
* Entry point for the Casono client application. Handles client startup and connection parameters.
|
||||||
* Handles client startup and connection parameters.
|
*
|
||||||
* <p>
|
* <p>Standardkonstruktor für die Anwendung.
|
||||||
* Standardkonstruktor für die Anwendung.
|
|
||||||
*/
|
*/
|
||||||
public class ClientApp {
|
public class ClientApp {
|
||||||
|
|
||||||
/**
|
private static final Logger LOGGER = LogManager.getLogger(ClientApp.class);
|
||||||
* Standardkonstruktor.
|
|
||||||
*/
|
/** Standardkonstruktor. */
|
||||||
public ClientApp() {
|
public ClientApp() {
|
||||||
// Standardkonstruktor
|
// Standardkonstruktor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the client application with the given address.
|
* Starts the client application with the given address.
|
||||||
|
*
|
||||||
* @param arg Address in the format "ip:port".
|
* @param arg Address in the format "ip:port".
|
||||||
* @throws IllegalArgumentException if the address format is invalid.
|
* @throws IllegalArgumentException if the address format is invalid.
|
||||||
*/
|
*/
|
||||||
@@ -32,7 +35,7 @@ public class ClientApp {
|
|||||||
String host = parts[0];
|
String host = parts[0];
|
||||||
int port = Integer.parseInt(parts[1]);
|
int port = Integer.parseInt(parts[1]);
|
||||||
|
|
||||||
System.out.println("You've selected the client. It will connect port " + port + " at host " + host);
|
LOGGER.info("You've selected the client. It will connect port {} at host {}", port, host);
|
||||||
Launcher.main(new String[]{});
|
Launcher.main(new String[] {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launcher for the Casono main UI.
|
* Launcher for the Casono main UI.
|
||||||
* <p>
|
*
|
||||||
* Standardkonstruktor für die Anwendung.
|
* <p>Standardkonstruktor für die Anwendung.
|
||||||
*/
|
*/
|
||||||
public class Launcher {
|
public class Launcher {
|
||||||
|
|
||||||
/**
|
/** Standardkonstruktor. */
|
||||||
* Standardkonstruktor.
|
|
||||||
*/
|
|
||||||
public Launcher() {
|
public Launcher() {
|
||||||
// Standardkonstruktor
|
// Standardkonstruktor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for launching the UI.
|
* Main entry point for launching the UI.
|
||||||
|
*
|
||||||
* @param args Command line arguments
|
* @param args Command line arguments
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Application.launch(ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui.class, args);
|
Application.launch(Casinomainui.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+29
-33
@@ -1,55 +1,49 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller-Klasse für das Chat-System innerhalb der Spieloberfläche.
|
* Controller-Klasse für das Chat-System innerhalb der Spieloberfläche.
|
||||||
*
|
*
|
||||||
* Verwaltet das Anzeigen von Chatnachrichten, das Eingabefeld für eigene Nachrichten
|
* <p>Verwaltet das Anzeigen von Chatnachrichten, das Eingabefeld für eigene Nachrichten sowie den
|
||||||
* sowie den Senden-Button. Unterstützt drei Arten von Nachrichten:
|
* Senden-Button. Unterstützt drei Arten von Nachrichten: - Player-to-Player (Privat) - Lobby-Chat
|
||||||
* - Player-to-Player (Privat)
|
* (Raum) - Globaler Chat (Serverweit)
|
||||||
* - Lobby-Chat (Raum)
|
|
||||||
* - Globaler Chat (Serverweit)
|
|
||||||
*
|
*
|
||||||
* Nachrichten werden in einem {@link VBox}-Container als {@link Label} angezeigt.
|
* <p>Nachrichten werden in einem {@link VBox}-Container als {@link Label} angezeigt. Eigene
|
||||||
* Eigene Nachrichten werden über {@link #onSendToNetwork(String)} an das
|
* Nachrichten werden über {@link #onSendToNetwork(String)} an das Netzwerkprotokoll weitergeleitet,
|
||||||
* Netzwerkprotokoll weitergeleitet, während eingehende Nachrichten über
|
* während eingehende Nachrichten über {@link #receiveMessage(String, String)} verarbeitet und
|
||||||
* {@link #receiveMessage(String, String)} verarbeitet und angezeigt werden.
|
* angezeigt werden.
|
||||||
*
|
*
|
||||||
* Hinweis: Einige TODOs stehen in der zugehörigen FXML-Datei
|
* <p>Hinweis: Einige TODOs stehen in der zugehörigen FXML-Datei
|
||||||
*/
|
*/
|
||||||
public class ChatController {
|
public class ChatController {
|
||||||
|
|
||||||
@FXML
|
@FXML private VBox chatVBox;
|
||||||
private VBox chatVBox;
|
|
||||||
|
|
||||||
@FXML
|
@FXML private TextField inputField;
|
||||||
private TextField inputField;
|
|
||||||
|
|
||||||
@FXML
|
@FXML private Button sendButton;
|
||||||
private Button sendButton;
|
|
||||||
|
|
||||||
@FXML
|
@FXML private ScrollPane chatScrollPane;
|
||||||
private ScrollPane chatScrollPane;
|
|
||||||
|
|
||||||
/**
|
private static final int CHAT_PADDING = 20;
|
||||||
* Initialisiert den ChatController nach dem Laden der FXML.
|
|
||||||
*/
|
/** Initialisiert den ChatController nach dem Laden der FXML. */
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
inputField.setOnAction(event -> sendMessage());
|
inputField.setOnAction(event -> sendMessage());
|
||||||
chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty());
|
chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Diese Methode wird vom Senden-Button oder Enter ausgelöst.
|
* Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an
|
||||||
* Sie gibt die eigene Nachricht an das Netzwerkprotokoll weiter.
|
* das Netzwerkprotokoll weiter.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void sendMessage() {
|
private void sendMessage() {
|
||||||
@@ -63,8 +57,9 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Diese Funktion muss vom Netzwerkprotokoll aufgerufen werden,
|
* Diese Funktion muss vom Netzwerkprotokoll aufgerufen werden, wenn eine neue Nachricht von
|
||||||
* wenn eine neue Nachricht von einem anderen Spieler kommt.
|
* einem anderen Spieler kommt.
|
||||||
|
*
|
||||||
* @param player Name des Spielers
|
* @param player Name des Spielers
|
||||||
* @param message Nachricht des Spielers
|
* @param message Nachricht des Spielers
|
||||||
*/
|
*/
|
||||||
@@ -73,13 +68,14 @@ public class ChatController {
|
|||||||
Label label = new Label("[" + time + "] " + player + ": " + message);
|
Label label = new Label("[" + time + "] " + player + ": " + message);
|
||||||
label.getStyleClass().add("info-text");
|
label.getStyleClass().add("info-text");
|
||||||
label.setWrapText(true); // Zeilenumbruch aktivieren
|
label.setWrapText(true); // Zeilenumbruch aktivieren
|
||||||
label.maxWidthProperty().bind(chatVBox.widthProperty().subtract(20));
|
label.maxWidthProperty().bind(chatVBox.widthProperty().subtract(CHAT_PADDING));
|
||||||
chatVBox.getChildren().add(label);
|
chatVBox.getChildren().add(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schnittstelle zum Netzwerkprotokoll.
|
* Schnittstelle zum Netzwerkprotokoll. Diese Funktion wird automatisch aufgerufen, wenn der
|
||||||
* Diese Funktion wird automatisch aufgerufen, wenn der Benutzer eine eigene Nachricht sendet.
|
* Benutzer eine eigene Nachricht sendet.
|
||||||
|
*
|
||||||
* @param message Nachricht, die der Benutzer abgeschickt hat
|
* @param message Nachricht, die der Benutzer abgeschickt hat
|
||||||
*/
|
*/
|
||||||
public void onSendToNetwork(String message) {
|
public void onSendToNetwork(String message) {
|
||||||
|
|||||||
+6
-19
@@ -1,38 +1,25 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui;
|
||||||
|
|
||||||
// import javafx.application.Platform;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
// import javafx.scene.control.TextField;
|
|
||||||
// import javafx.scene.input.KeyCode;
|
|
||||||
// import javafx.scene.input.KeyEvent;
|
|
||||||
// import javafx.scene.input.MouseEvent;
|
|
||||||
// import javafx.scene.layout.HBox;
|
|
||||||
// import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
// import javafx.stage.Stage;
|
|
||||||
// import javafx.scene.Scene;
|
|
||||||
// import javafx.scene.web.WebView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller für die Casino-Spielfläche.
|
* Controller für die Casino-Spielfläche.
|
||||||
*
|
*
|
||||||
* Verantwortlich für:
|
* <p>Verantwortlich für: - die Darstellung des Pokertisches und der Spieleroberfläche, - die
|
||||||
* - die Darstellung des Pokertisches und der Spieleroberfläche,
|
* Verarbeitung von Benutzereingaben, - die Schnittstelle zur GameEngine und zum Netzwerkprotokoll.
|
||||||
* - die Verarbeitung von Benutzereingaben,
|
|
||||||
* - die Schnittstelle zur GameEngine und zum Netzwerkprotokoll.
|
|
||||||
*
|
*
|
||||||
* Hinweise:
|
* <p>Hinweise: - Die Methode `onTableClick()` dient aktuell nur als Test-Logik. Sie ist ggf. nicht
|
||||||
* - Die Methode `onTableClick()` dient aktuell nur als Test-Logik.
|
* mehr funktionsfähig und wird zukünftig durch die finale Spielinteraktion ersetzt.
|
||||||
* Sie ist ggf. nicht mehr funktionsfähig und wird zukünftig
|
|
||||||
* durch die finale Spielinteraktion ersetzt.
|
|
||||||
*/
|
*/
|
||||||
public class CasinoGameController {
|
public class CasinoGameController {
|
||||||
|
|
||||||
@FXML private Label welcomeText;
|
@FXML private Label welcomeText;
|
||||||
@FXML private VBox casinoTable;
|
@FXML private VBox casinoTable;
|
||||||
|
|
||||||
// TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt, sobald die GameEngine fertig ist
|
// TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt,
|
||||||
|
// sobald die GameEngine fertig ist
|
||||||
@FXML
|
@FXML
|
||||||
public void onTableClick() {
|
public void onTableClick() {
|
||||||
welcomeText.setText("Einsatz akzeptiert!");
|
welcomeText.setText("Einsatz akzeptiert!");
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hauptklasse für das Casino-Spiel-UI.
|
* Hauptklasse für das Casino-Spiel-UI.
|
||||||
*
|
*
|
||||||
* Startet die JavaFX-Anwendung, lädt die grafische Oberfläche aus der FXML-Datei
|
* <p>Startet die JavaFX-Anwendung, lädt die grafische Oberfläche aus der FXML-Datei und
|
||||||
* und initialisiert die Haupt-Stage für das Spiel.
|
* initialisiert die Haupt-Stage für das Spiel.
|
||||||
*
|
*
|
||||||
* Aufgaben:
|
* <p>Aufgaben: - Lädt die FXML-Oberfläche "/ui-structure/Casinogameui.fxml". - Lädt das
|
||||||
* - Lädt die FXML-Oberfläche "/ui-structure/Casinogameui.fxml".
|
* Anwendungs-Icon aus "/images/logoinverted.png". - Startet die Anwendung im Vollbildmodus.
|
||||||
* - Lädt das Anwendungs-Icon aus "/images/logoinverted.png".
|
|
||||||
* - Startet die Anwendung im Vollbildmodus.
|
|
||||||
*/
|
*/
|
||||||
public class CasinoGameUI extends Application {
|
public class CasinoGameUI extends Application {
|
||||||
|
|
||||||
|
private static final int DEFAULT_WIDTH = 1200;
|
||||||
|
private static final int DEFAULT_HEIGHT = 800;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Startet die Haupt-Stage der Anwendung.
|
* Startet die Haupt-Stage der Anwendung.
|
||||||
*
|
*
|
||||||
@@ -28,11 +28,13 @@ public class CasinoGameUI extends Application {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/casinogameui.fxml"));
|
FXMLLoader fxmlLoader =
|
||||||
Scene scene = new Scene(fxmlLoader.load(), 1200, 800);
|
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/casinogameui.fxml"));
|
||||||
|
Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
stage.setTitle("Casono (GAME)");
|
stage.setTitle("Casono (GAME)");
|
||||||
|
|
||||||
stage.getIcons().add(new javafx.scene.image.Image(getClass().getResource("/images/logoinverted.png").toExternalForm()));
|
String iconPath = getClass().getResource("/images/logoinverted.png").toExternalForm();
|
||||||
|
stage.getIcons().add(new javafx.scene.image.Image(iconPath));
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.setFullScreen(true);
|
stage.setFullScreen(true);
|
||||||
stage.show();
|
stage.show();
|
||||||
@@ -40,6 +42,7 @@ public class CasinoGameUI extends Application {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Startpunkt der Anwendung.
|
* Startpunkt der Anwendung.
|
||||||
|
*
|
||||||
* @param args Befehlszeilenargumente.
|
* @param args Befehlszeilenargumente.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
+424
-246
@@ -1,58 +1,75 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
|
||||||
|
|
||||||
|
import java.net.CookieHandler;
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.CookiePolicy;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
import javafx.scene.web.WebEngine;
|
import javafx.scene.web.WebEngine;
|
||||||
import javafx.scene.web.WebView;
|
import javafx.scene.web.WebView;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimenteller integrierter Browser für Casono.
|
* Experimenteller integrierter Browser für Casono.
|
||||||
*
|
*
|
||||||
* Diese Klasse implementiert einen einfachen eingebetteten Webbrowser
|
* <p>Diese Klasse implementiert einen einfachen eingebetteten Webbrowser auf Basis von {@link
|
||||||
* auf Basis von {@link javafx.scene.web.WebView}. Der Browser dient
|
* javafx.scene.web.WebView}. Der Browser dient primär als Hilfswerkzeug innerhalb des Spiels, um
|
||||||
* primär als Hilfswerkzeug innerhalb des Spiels, um externe Inhalte
|
* externe Inhalte wie Webseiten oder Videos anzuzeigen.
|
||||||
* wie Webseiten oder Videos anzuzeigen.
|
|
||||||
*
|
*
|
||||||
* Status
|
* <p>Status Der Browser befindet sich derzeit in einer experimentellen Phase. Einige
|
||||||
* Der Browser befindet sich derzeit in einer experimentellen Phase.
|
* Sicherheitsmechanismen basieren auf experimentellen KI-gestützten Empfehlungen und können sich in
|
||||||
* Einige Sicherheitsmechanismen basieren auf experimentellen
|
* zukünftigen Versionen noch ändern.
|
||||||
* KI-gestützten Empfehlungen und können sich in zukünftigen
|
|
||||||
* Versionen noch ändern.
|
|
||||||
*
|
*
|
||||||
* Zweck
|
* <p>Zweck Der Browser wird aktuell experimentell genutzt, um: Pokerregeln direkt im Spiel zu
|
||||||
* Der Browser wird aktuell experimentell genutzt, um:
|
* erklären Hilfeseiten oder Dokumentationen anzuzeigen Videos (z.B. Tutorials oder Erklärungen)
|
||||||
* Pokerregeln direkt im Spiel zu erklären
|
* über Plattformen wie YouTube abzuspielen
|
||||||
* Hilfeseiten oder Dokumentationen anzuzeigen
|
|
||||||
* Videos (z.B. Tutorials oder Erklärungen) über Plattformen wie YouTube abzuspielen
|
|
||||||
*
|
*
|
||||||
* Sicherheitsmechanismen
|
* <p>Sicherheitsmechanismen Da externe Webseiten geladen werden können, wurden einige grundlegende
|
||||||
* Da externe Webseiten geladen werden können, wurden einige
|
* Schutzmaßnahmen integriert: - HTTPS-Zwang für Webseiten - Whitelist für bekannte Domains -
|
||||||
* grundlegende Schutzmaßnahmen integriert:
|
* Warnung bei unbekannten Webseiten - JavaScript standardmäßig deaktiviert (man kann es jedoch für
|
||||||
* - HTTPS-Zwang für Webseiten
|
* Google etc. einschalten) - Popup-Blocker - Automatische Cookie-Löschung beim Schließen
|
||||||
* - Whitelist für bekannte Domains
|
|
||||||
* - Warnung bei unbekannten Webseiten
|
|
||||||
* - JavaScript standardmäßig deaktiviert (man kann es jedoch für Google etc. einschalten)
|
|
||||||
* - Popup-Blocker
|
|
||||||
* - Automatische Cookie-Löschung beim Schließen
|
|
||||||
*/
|
*/
|
||||||
public class CasinoBrowserController {
|
public class CasinoBrowserController {
|
||||||
|
|
||||||
private static final Set<String> TRUSTED_DOMAINS = new HashSet<>();
|
private static final Set<String> TRUSTED_DOMAINS = new HashSet<>();
|
||||||
|
|
||||||
private static final CookieManager COOKIE_MANAGER =
|
private static final CookieManager COOKIE_MANAGER =
|
||||||
new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||||
|
|
||||||
|
private static final org.apache.logging.log4j.Logger LOGGER =
|
||||||
|
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
|
||||||
|
|
||||||
|
private static final int LOGO_HEIGHT = 40;
|
||||||
|
private static final int CORNER_RADIUS = 40;
|
||||||
|
private static final int HBOX_SPACIN = 10;
|
||||||
|
private static final int VBOX_SPACING = 10;
|
||||||
|
private static final int PADDING_SIZE = 10;
|
||||||
|
private static final int WINDOW_WIDTH = 1200;
|
||||||
|
private static final int WINDOW_HEIGHT = 800;
|
||||||
|
private static final int ALERT_WIDTH = 600;
|
||||||
|
private static final int ALERT_HEIGHT = 300;
|
||||||
|
private static final String LOGO_PATH = "/images/logoinverted.png";
|
||||||
|
private static final String LOGO_PATH_MAIN = "/images/logo.png";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CookieHandler.setDefault(COOKIE_MANAGER);
|
CookieHandler.setDefault(COOKIE_MANAGER);
|
||||||
|
|
||||||
@@ -78,220 +95,367 @@ public class CasinoBrowserController {
|
|||||||
|
|
||||||
private static boolean javascriptEnabled = false;
|
private static boolean javascriptEnabled = false;
|
||||||
|
|
||||||
/**
|
/** Löscht alle gespeicherten Cookies der aktuellen Browser-Sitzung. */
|
||||||
* Löscht alle gespeicherten Cookies der aktuellen Browser-Sitzung.
|
|
||||||
*/
|
|
||||||
private static void clearCookies() {
|
private static void clearCookies() {
|
||||||
try {
|
try {
|
||||||
COOKIE_MANAGER.getCookieStore().removeAll();
|
COOKIE_MANAGER.getCookieStore().removeAll();
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Öffnet ein neues Browserfenster und lädt eine angegebene Webseite.
|
* Öffnet ein neues Browserfenster und lädt eine angegebene Webseite.
|
||||||
*
|
*
|
||||||
* Falls die Webseite nicht zur Liste vertrauenswürdiger Domains gehört,
|
* <p>Falls die Webseite nicht zur Liste vertrauenswürdiger Domains gehört, wird der Benutzer
|
||||||
* wird der Benutzer gefragt, ob die Seite dennoch geladen werden soll.
|
* gefragt, ob die Seite dennoch geladen werden soll.
|
||||||
*
|
*
|
||||||
* @param url die Startadresse der Webseite, die geladen werden soll
|
* @param url die Startadresse der Webseite, die geladen werden soll
|
||||||
*/
|
*/
|
||||||
public static void open(String url) {
|
public static void open(String url) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(
|
||||||
Stage stage = new Stage();
|
() -> {
|
||||||
|
Stage stage = new Stage();
|
||||||
|
|
||||||
WebView webView = new WebView();
|
WebView webView = new WebView();
|
||||||
WebEngine engine = webView.getEngine();
|
WebEngine engine = webView.getEngine();
|
||||||
|
|
||||||
engine.setJavaScriptEnabled(false);
|
engine.setJavaScriptEnabled(false);
|
||||||
|
|
||||||
// Popup Blocker
|
configurePopupBlocker(engine);
|
||||||
engine.setCreatePopupHandler(config -> {
|
|
||||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
|
||||||
alert.setTitle("Popup blockiert");
|
|
||||||
alert.setHeaderText(null);
|
|
||||||
alert.setContentText("Popup wurde aus Sicherheitsgründen blockiert.");
|
|
||||||
|
|
||||||
try {
|
webView.getStyleClass().add("web-view");
|
||||||
Image logo =
|
|
||||||
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png"));
|
|
||||||
Image logomain =
|
|
||||||
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logo.png"));
|
|
||||||
|
|
||||||
if (logo != null) {
|
StackPane webContainer = createWebContainer(webView);
|
||||||
Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow();
|
|
||||||
if (alertStage != null) {
|
|
||||||
alertStage.getIcons().add(logo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logomain != null) {
|
Rectangle clip = new Rectangle();
|
||||||
ImageView logoView = new ImageView(logomain);
|
clip.setArcWidth(CORNER_RADIUS);
|
||||||
logoView.setFitHeight(40);
|
clip.setArcHeight(CORNER_RADIUS);
|
||||||
logoView.setPreserveRatio(true);
|
webView.setClip(clip);
|
||||||
alert.setGraphic(logoView);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Logo konnte nicht geladen werden");
|
|
||||||
}
|
|
||||||
|
|
||||||
alert.showAndWait();
|
webView.widthProperty()
|
||||||
return null;
|
.addListener((o, a, b) -> clip.setWidth(b.doubleValue()));
|
||||||
});
|
webView.heightProperty()
|
||||||
|
.addListener((o, a, b) -> clip.setHeight(b.doubleValue()));
|
||||||
|
|
||||||
webView.getStyleClass().add("web-view");
|
ImageView browserLogo = loadLogos(stage);
|
||||||
|
|
||||||
StackPane webContainer = new StackPane(webView);
|
TextField urlField = createUrlField(url);
|
||||||
webContainer.getStyleClass().add("browser-web-view");
|
Label securityLabel = createSecurityLabel();
|
||||||
VBox.setVgrow(webContainer, Priority.ALWAYS);
|
|
||||||
|
|
||||||
Rectangle clip = new Rectangle();
|
Button jsToggle = createJsToggle(engine);
|
||||||
clip.setArcWidth(40);
|
Button backBtn = createBackButton(engine);
|
||||||
clip.setArcHeight(40);
|
Button fwdBtn = createForwardButton(engine);
|
||||||
webView.setClip(clip);
|
Button reloadBtn = createReloadButton(engine);
|
||||||
|
Button closeBtn = createCloseButton(stage, webView);
|
||||||
|
configureUrlEvents(engine, urlField, securityLabel);
|
||||||
|
|
||||||
webView.widthProperty().addListener((o,a,b)->clip.setWidth(b.doubleValue()));
|
HBox taskbar =
|
||||||
webView.heightProperty().addListener((o,a,b)->clip.setHeight(b.doubleValue()));
|
new HBox(
|
||||||
|
HBOX_SPACIN,
|
||||||
|
browserLogo,
|
||||||
|
backBtn,
|
||||||
|
fwdBtn,
|
||||||
|
reloadBtn,
|
||||||
|
urlField,
|
||||||
|
jsToggle,
|
||||||
|
securityLabel,
|
||||||
|
closeBtn);
|
||||||
|
|
||||||
ImageView browserLogo = new ImageView();
|
taskbar.getStyleClass().add("taskbar-browser");
|
||||||
|
taskbar.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
try {
|
Scene scene = createScene(taskbar, webContainer);
|
||||||
Image logo =
|
|
||||||
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png"));
|
|
||||||
Image logomain =
|
|
||||||
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logo.png"));
|
|
||||||
|
|
||||||
if (logo != null) {
|
configureKeyEvents(scene, engine);
|
||||||
stage.getIcons().add(logo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logomain != null) {
|
stage.setScene(scene);
|
||||||
browserLogo.setImage(logomain);
|
stage.setTitle("Casono Browser");
|
||||||
browserLogo.setFitHeight(30);
|
loadUrlSafely(engine, url, securityLabel);
|
||||||
browserLogo.setPreserveRatio(true);
|
stage.show();
|
||||||
}
|
});
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Logo konnte nicht geladen werden");
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL Feld
|
|
||||||
TextField urlField = new TextField(url);
|
|
||||||
urlField.getStyleClass().add("gray-input-field");
|
|
||||||
HBox.setHgrow(urlField, Priority.ALWAYS);
|
|
||||||
|
|
||||||
Label securityLabel = new Label("SICHER");
|
|
||||||
securityLabel.getStyleClass().add("security-label");
|
|
||||||
|
|
||||||
// JS Toggle
|
|
||||||
Button jsToggle = new Button("JS EINSCHALTEN");
|
|
||||||
jsToggle.getStyleClass().add("red-button");
|
|
||||||
|
|
||||||
jsToggle.setOnAction(e -> {
|
|
||||||
javascriptEnabled = !javascriptEnabled;
|
|
||||||
engine.setJavaScriptEnabled(javascriptEnabled);
|
|
||||||
|
|
||||||
if (javascriptEnabled) {
|
|
||||||
jsToggle.setText("JS AUSSCHALTEN");
|
|
||||||
jsToggle.getStyleClass().removeAll("red-button");
|
|
||||||
jsToggle.getStyleClass().add("yellow-button");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
jsToggle.setText("JS EINSCHALTEN");
|
|
||||||
jsToggle.getStyleClass().removeAll("yellow-button");
|
|
||||||
jsToggle.getStyleClass().add("red-button");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Navigation
|
|
||||||
Button backBtn = new Button("<");
|
|
||||||
backBtn.getStyleClass().add("gray-button");
|
|
||||||
backBtn.setOnAction(e -> {
|
|
||||||
if(engine.getHistory().getCurrentIndex() > 0){
|
|
||||||
engine.getHistory().go(-1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button fwdBtn = new Button(">");
|
|
||||||
fwdBtn.getStyleClass().add("gray-button");
|
|
||||||
fwdBtn.setOnAction(e -> {
|
|
||||||
if(engine.getHistory().getCurrentIndex() <
|
|
||||||
engine.getHistory().getEntries().size()-1){
|
|
||||||
engine.getHistory().go(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reload Button
|
|
||||||
Button reloadBtn = new Button("⟳");
|
|
||||||
reloadBtn.getStyleClass().add("gray-button");
|
|
||||||
reloadBtn.setOnAction(e -> engine.reload());
|
|
||||||
|
|
||||||
// Close
|
|
||||||
Button closeBtn = new Button("X");
|
|
||||||
closeBtn.getStyleClass().add("red-button");
|
|
||||||
closeBtn.setOnAction(e -> {
|
|
||||||
webView.getEngine().load("about:blank");
|
|
||||||
clearCookies();
|
|
||||||
stage.close();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// URL Enter
|
|
||||||
urlField.setOnAction(e ->
|
|
||||||
loadUrlSafely(engine, urlField.getText(), securityLabel));
|
|
||||||
engine.locationProperty().addListener((obs,o,n)->urlField.setText(n));
|
|
||||||
|
|
||||||
// Taskbar
|
|
||||||
HBox taskbar = new HBox(
|
|
||||||
10,
|
|
||||||
browserLogo,
|
|
||||||
backBtn,
|
|
||||||
fwdBtn,
|
|
||||||
reloadBtn,
|
|
||||||
urlField,
|
|
||||||
jsToggle,
|
|
||||||
securityLabel,
|
|
||||||
closeBtn
|
|
||||||
);
|
|
||||||
|
|
||||||
taskbar.getStyleClass().add("taskbar-browser");
|
|
||||||
taskbar.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// Root
|
|
||||||
VBox root = new VBox(10, taskbar, webContainer);
|
|
||||||
root.getStyleClass().add("browser-root");
|
|
||||||
root.setPadding(new Insets(10));
|
|
||||||
Scene scene = new Scene(root,1200,800);
|
|
||||||
var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css");
|
|
||||||
|
|
||||||
if(css != null) {
|
|
||||||
scene.getStylesheets().add(css.toExternalForm());
|
|
||||||
}
|
|
||||||
|
|
||||||
// F5 Reload
|
|
||||||
scene.setOnKeyPressed(event -> {
|
|
||||||
switch (event.getCode()) {
|
|
||||||
case F5 -> engine.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.setScene(scene);
|
|
||||||
stage.setTitle("Casono Browser");
|
|
||||||
loadUrlSafely(engine, url, securityLabel);
|
|
||||||
stage.show();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt eine URL in den Browser, nachdem grundlegende Sicherheitsprüfungen
|
* WebView Container
|
||||||
* durchgeführt wurden.
|
|
||||||
*
|
*
|
||||||
* Vor dem Laden einer Seite werden folgende Prüfungen durchgeführt:
|
* @param webView WebView Inhalt
|
||||||
* - Überprüfung des Protokolls (nur HTTPS erlaubt)
|
* @return StackPane Container
|
||||||
* - Überprüfung der Domain gegen eine Whitelist
|
*/
|
||||||
* - Schutz vor Domain-Spoofing (Domain-Vortäuschung)
|
public static StackPane createWebContainer(WebView webView) {
|
||||||
|
StackPane webContainer = new StackPane(webView);
|
||||||
|
webContainer.getStyleClass().add("browser-web-view");
|
||||||
|
VBox.setVgrow(webContainer, Priority.ALWAYS);
|
||||||
|
|
||||||
|
return webContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup Blocker
|
||||||
*
|
*
|
||||||
* Falls eine Domain nicht als vertrauenswürdig eingestuft wird,
|
* @param engine WebEngine nutzen
|
||||||
* muss der Benutzer bestätigen, dass die Seite dennoch geöffnet
|
*/
|
||||||
* werden darf.
|
public static void configurePopupBlocker(WebEngine engine) {
|
||||||
|
engine.setCreatePopupHandler(
|
||||||
|
config -> {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||||
|
alert.setTitle("Popup blockiert");
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText("Popup wurde aus Sicherheitsgründen blockiert.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
|
Image logo = new Image(stream);
|
||||||
|
|
||||||
|
// Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
|
||||||
|
// einzuhalten
|
||||||
|
var streamM =
|
||||||
|
CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
||||||
|
Image logomain = new Image(streamM);
|
||||||
|
|
||||||
|
if (logo != null) {
|
||||||
|
Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow();
|
||||||
|
if (alertStage != null) {
|
||||||
|
alertStage.getIcons().add(logo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logomain != null) {
|
||||||
|
ImageView logoView = new ImageView(logomain);
|
||||||
|
logoView.setFitHeight(LOGO_HEIGHT);
|
||||||
|
logoView.setPreserveRatio(true);
|
||||||
|
alert.setGraphic(logoView);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Logo konnte nicht geladen werden");
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.showAndWait();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logos laden
|
||||||
|
*
|
||||||
|
* @param stage Fenster Stage
|
||||||
|
* @return ImageView Logo
|
||||||
|
*/
|
||||||
|
private static ImageView loadLogos(Stage stage) {
|
||||||
|
ImageView browserLogo = new ImageView();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
|
Image logo = new Image(stream);
|
||||||
|
|
||||||
|
// Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
|
||||||
|
// einzuhalten
|
||||||
|
var streamM = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
||||||
|
Image logomain = new Image(streamM);
|
||||||
|
|
||||||
|
if (logo != null) {
|
||||||
|
stage.getIcons().add(logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logomain != null) {
|
||||||
|
browserLogo.setImage(logomain);
|
||||||
|
browserLogo.setFitHeight(LOGO_HEIGHT);
|
||||||
|
browserLogo.setPreserveRatio(true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Logo konnte nicht geladen werden");
|
||||||
|
}
|
||||||
|
|
||||||
|
return browserLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL Feld
|
||||||
|
*
|
||||||
|
* @param url Start URL
|
||||||
|
* @return TextField Eingabe
|
||||||
|
*/
|
||||||
|
public static TextField createUrlField(String url) {
|
||||||
|
TextField urlField = new TextField(url);
|
||||||
|
urlField.getStyleClass().add("gray-input-field");
|
||||||
|
HBox.setHgrow(urlField, Priority.ALWAYS);
|
||||||
|
|
||||||
|
return urlField;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sicherheits Label
|
||||||
|
*
|
||||||
|
* @return Label Anzeige
|
||||||
|
*/
|
||||||
|
public static Label createSecurityLabel() {
|
||||||
|
Label securityLabel = new Label("SICHER");
|
||||||
|
securityLabel.getStyleClass().add("security-label");
|
||||||
|
|
||||||
|
return securityLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS Toggle
|
||||||
|
*
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
* @return Button Umschalten
|
||||||
|
*/
|
||||||
|
public static Button createJsToggle(WebEngine engine) {
|
||||||
|
Button jsToggle = new Button("JS EINSCHALTEN");
|
||||||
|
jsToggle.getStyleClass().add("red-button");
|
||||||
|
|
||||||
|
jsToggle.setOnAction(
|
||||||
|
e -> {
|
||||||
|
javascriptEnabled = !javascriptEnabled;
|
||||||
|
engine.setJavaScriptEnabled(javascriptEnabled);
|
||||||
|
|
||||||
|
if (javascriptEnabled) {
|
||||||
|
jsToggle.setText("JS AUSSCHALTEN");
|
||||||
|
jsToggle.getStyleClass().removeAll("red-button");
|
||||||
|
jsToggle.getStyleClass().add("yellow-button");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
jsToggle.setText("JS EINSCHALTEN");
|
||||||
|
jsToggle.getStyleClass().removeAll("yellow-button");
|
||||||
|
jsToggle.getStyleClass().add("red-button");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return jsToggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zurück Button
|
||||||
|
*
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
* @return Button Zurück
|
||||||
|
*/
|
||||||
|
public static Button createBackButton(WebEngine engine) {
|
||||||
|
Button backBtn = new Button("<");
|
||||||
|
backBtn.getStyleClass().add("gray-button");
|
||||||
|
backBtn.setOnAction(
|
||||||
|
e -> {
|
||||||
|
if (engine.getHistory().getCurrentIndex() > 0) {
|
||||||
|
engine.getHistory().go(-1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return backBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vorwärts Button
|
||||||
|
*
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
* @return Button Vorwärts
|
||||||
|
*/
|
||||||
|
public static Button createForwardButton(WebEngine engine) {
|
||||||
|
Button fwdBtn = new Button(">");
|
||||||
|
fwdBtn.getStyleClass().add("gray-button");
|
||||||
|
fwdBtn.setOnAction(
|
||||||
|
e -> {
|
||||||
|
if (engine.getHistory().getCurrentIndex()
|
||||||
|
< engine.getHistory().getEntries().size() - 1) {
|
||||||
|
engine.getHistory().go(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fwdBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload Button
|
||||||
|
*
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
* @return Button Reload
|
||||||
|
*/
|
||||||
|
public static Button createReloadButton(WebEngine engine) {
|
||||||
|
Button reloadBtn = new Button("⟳");
|
||||||
|
reloadBtn.getStyleClass().add("gray-button");
|
||||||
|
reloadBtn.setOnAction(e -> engine.reload());
|
||||||
|
return reloadBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close Button
|
||||||
|
*
|
||||||
|
* @param stage Fenster Stage
|
||||||
|
* @param webView WebView Inhalt
|
||||||
|
* @return Button Schließen
|
||||||
|
*/
|
||||||
|
public static Button createCloseButton(Stage stage, WebView webView) {
|
||||||
|
Button closeBtn = new Button("X");
|
||||||
|
closeBtn.getStyleClass().add("red-button");
|
||||||
|
closeBtn.setOnAction(
|
||||||
|
e -> {
|
||||||
|
webView.getEngine().load("about:blank");
|
||||||
|
clearCookies();
|
||||||
|
stage.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
return closeBtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL Events
|
||||||
|
*
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
* @param urlField URL Textfeld
|
||||||
|
* @param securityLabel Sicherheits Label
|
||||||
|
*/
|
||||||
|
public static void configureUrlEvents(
|
||||||
|
WebEngine engine, TextField urlField, Label securityLabel) {
|
||||||
|
urlField.setOnAction(e -> loadUrlSafely(engine, urlField.getText(), securityLabel));
|
||||||
|
engine.locationProperty().addListener((obs, o, n) -> urlField.setText(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scene erstellen
|
||||||
|
*
|
||||||
|
* @param taskbar Taskbar HBox
|
||||||
|
* @param webContainer Web Container
|
||||||
|
* @return Scene Fenster
|
||||||
|
*/
|
||||||
|
public static Scene createScene(HBox taskbar, StackPane webContainer) {
|
||||||
|
VBox root = new VBox(VBOX_SPACING, taskbar, webContainer);
|
||||||
|
root.getStyleClass().add("browser-root");
|
||||||
|
root.setPadding(new Insets(PADDING_SIZE));
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
|
||||||
|
var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css");
|
||||||
|
|
||||||
|
if (css != null) {
|
||||||
|
scene.getStylesheets().add(css.toExternalForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key Events
|
||||||
|
*
|
||||||
|
* @param scene Scene Fenster
|
||||||
|
* @param engine WebEngine nutzen
|
||||||
|
*/
|
||||||
|
public static void configureKeyEvents(Scene scene, WebEngine engine) {
|
||||||
|
scene.setOnKeyPressed(
|
||||||
|
event -> {
|
||||||
|
if (event.getCode() == KeyCode.F5) {
|
||||||
|
engine.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt eine URL in den Browser, nachdem grundlegende Sicherheitsprüfungen durchgeführt wurden.
|
||||||
|
*
|
||||||
|
* <p>Vor dem Laden einer Seite werden folgende Prüfungen durchgeführt: - Überprüfung des
|
||||||
|
* Protokolls (nur HTTPS erlaubt) - Überprüfung der Domain gegen eine Whitelist - Schutz vor
|
||||||
|
* Domain-Spoofing (Domain-Vortäuschung)
|
||||||
|
*
|
||||||
|
* <p>Falls eine Domain nicht als vertrauenswürdig eingestuft wird, muss der Benutzer
|
||||||
|
* bestätigen, dass die Seite dennoch geöffnet werden darf.
|
||||||
*
|
*
|
||||||
* @param engine der WebEngine-Renderer des Browsers
|
* @param engine der WebEngine-Renderer des Browsers
|
||||||
* @param url die zu ladende Webadresse
|
* @param url die zu ladende Webadresse
|
||||||
@@ -299,66 +463,80 @@ public class CasinoBrowserController {
|
|||||||
*/
|
*/
|
||||||
private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) {
|
private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) {
|
||||||
try {
|
try {
|
||||||
if(!url.startsWith("http")) {
|
if (!url.startsWith("http")) {
|
||||||
url = "https://" + url;
|
url = "https://" + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
URI uri = new URI(url);
|
URI uri = new URI(url);
|
||||||
|
|
||||||
// HTTPS Pflicht
|
// HTTPS Pflicht
|
||||||
if(!"https".equalsIgnoreCase(uri.getScheme())) {
|
if (!"https".equalsIgnoreCase(uri.getScheme())) {
|
||||||
securityLabel.setText("BLOCKIERT");
|
securityLabel.setText("BLOCKIERT");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String host = uri.getHost();
|
String host = uri.getHost();
|
||||||
if(host == null) {
|
if (host == null) {
|
||||||
securityLabel.setText("ERROR");
|
securityLabel.setText("ERROR");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sicherer Domain Check
|
// Sicherer Domain Check
|
||||||
boolean trusted = TRUSTED_DOMAINS.stream().anyMatch(domain ->
|
boolean trusted =
|
||||||
host.equals(domain) || host.endsWith("." + domain));
|
TRUSTED_DOMAINS.stream()
|
||||||
|
.anyMatch(domain -> host.equals(domain) || host.endsWith("." + domain));
|
||||||
|
|
||||||
if(!trusted) {
|
if (!trusted) {
|
||||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
if (!showUnknownWebsiteAlert(host)) {
|
||||||
|
|
||||||
alert.setTitle("Unbekannte Website");
|
|
||||||
alert.setHeaderText("Diese Website ist nicht bekannt");
|
|
||||||
alert.setContentText(
|
|
||||||
host + "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?"
|
|
||||||
);
|
|
||||||
|
|
||||||
Image logo = new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png"));
|
|
||||||
if (logo != null) {
|
|
||||||
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
|
|
||||||
if(stage != null) {
|
|
||||||
stage.getIcons().add(logo);
|
|
||||||
}
|
|
||||||
ImageView logomain = new ImageView(logo);
|
|
||||||
logomain.setFitHeight(50);
|
|
||||||
logomain.setPreserveRatio(true);
|
|
||||||
alert.setGraphic(logomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
alert.getDialogPane().setPrefSize(600,300);
|
|
||||||
Optional<ButtonType> result = alert.showAndWait();
|
|
||||||
|
|
||||||
if(result.isEmpty() || result.get() != ButtonType.OK) {
|
|
||||||
securityLabel.setText("BLOCKIERT");
|
securityLabel.setText("BLOCKIERT");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
securityLabel.setText("UNBEKANNT");
|
securityLabel.setText("UNBEKANNT");
|
||||||
} else {
|
} else {
|
||||||
securityLabel.setText("SICHER");
|
securityLabel.setText("SICHER");
|
||||||
}
|
}
|
||||||
engine.load(uri.toString());
|
engine.load(uri.toString());
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
|
||||||
catch (Exception e) {
|
|
||||||
securityLabel.setText("ERROR");
|
securityLabel.setText("ERROR");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt Warnung an.
|
||||||
|
*
|
||||||
|
* @param host Website Host
|
||||||
|
* @return OK gedrückt
|
||||||
|
*/
|
||||||
|
private static boolean showUnknownWebsiteAlert(String host) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
|
||||||
|
alert.setTitle("Unbekannte Website");
|
||||||
|
alert.setHeaderText("Diese Website ist nicht bekannt");
|
||||||
|
alert.setContentText(
|
||||||
|
host
|
||||||
|
+ "\n\nDiese Seite ist nicht vom "
|
||||||
|
+ "Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?");
|
||||||
|
|
||||||
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
|
Image logo = new Image(stream);
|
||||||
|
if (logo != null) {
|
||||||
|
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
|
||||||
|
if (stage != null) {
|
||||||
|
stage.getIcons().add(logo);
|
||||||
|
}
|
||||||
|
ImageView logomain = new ImageView(logo);
|
||||||
|
logomain.setFitHeight(LOGO_HEIGHT);
|
||||||
|
logomain.setPreserveRatio(true);
|
||||||
|
alert.setGraphic(logomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.getDialogPane().setPrefSize(ALERT_WIDTH, ALERT_HEIGHT);
|
||||||
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
|
|
||||||
|
if (result.isEmpty() || result.get() != ButtonType.OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+32
-27
@@ -11,22 +11,27 @@ import javafx.scene.layout.HBox;
|
|||||||
/**
|
/**
|
||||||
* Controller für die interaktive Taskleiste innerhalb der Poker-UI.
|
* Controller für die interaktive Taskleiste innerhalb der Poker-UI.
|
||||||
*
|
*
|
||||||
* Verantwortlich für:
|
* <p>Verantwortlich für: - Drag-and-Drop-Verschieben der Taskleiste, - Eingabe und Verwaltung von
|
||||||
* - Drag-and-Drop-Verschieben der Taskleiste,
|
* Spieleinsätzen, - Steuerung allgemeiner Menüfunktionen wie Exit.
|
||||||
* - Eingabe und Verwaltung von Spieleinsätzen,
|
|
||||||
* - Steuerung allgemeiner Menüfunktionen wie Exit.
|
|
||||||
*/
|
*/
|
||||||
public class TaskbarController {
|
public class TaskbarController {
|
||||||
|
|
||||||
|
private static final org.apache.logging.log4j.Logger LOGGER =
|
||||||
|
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
|
||||||
|
|
||||||
@FXML private HBox taskbar;
|
@FXML private HBox taskbar;
|
||||||
@FXML private TextField taskbarInput;
|
@FXML private TextField taskbarInput;
|
||||||
|
|
||||||
private double xOffset = 0;
|
private double xOffset = 0;
|
||||||
private double yOffset = 0;
|
private double yOffset = 0;
|
||||||
|
private static final double TASKBAR_SCALE = 0.9;
|
||||||
|
private static final int MIN_CREDITS = 5;
|
||||||
|
private static final int MAX_CREDITS = 100000;
|
||||||
|
private static final int CREDIT_STEP = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn die Taskleiste mit der Maus gedrückt wird.
|
* Wird aufgerufen, wenn die Taskleiste mit der Maus gedrückt wird. Speichert die relative
|
||||||
* Speichert die relative Position, um später korrekt zu verschieben.
|
* Position, um später korrekt zu verschieben.
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event Das Mausereignis
|
||||||
*/
|
*/
|
||||||
@@ -37,10 +42,11 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, während die Taskleiste mit der Maus gezogen wird.
|
* Wird aufgerufen, während die Taskleiste mit der Maus gezogen wird. Aktualisiert die Position
|
||||||
* Aktualisiert die Position und skaliert die Taskleiste leicht zur visuellen Rückmeldung.
|
* und skaliert die Taskleiste leicht zur visuellen Rückmeldung.
|
||||||
*
|
*
|
||||||
* TODO: Es muss noch gefixt werden, dass die Taskleiste nicht aus dem Fenster verschwinden kann.
|
* <p>TODO: Es muss noch gefixt werden, dass die Taskleiste nicht aus dem Fenster verschwinden
|
||||||
|
* kann.
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event Das Mausereignis
|
||||||
*/
|
*/
|
||||||
@@ -49,13 +55,13 @@ public class TaskbarController {
|
|||||||
taskbar.setLayoutX(event.getSceneX() - xOffset);
|
taskbar.setLayoutX(event.getSceneX() - xOffset);
|
||||||
taskbar.setLayoutY(event.getSceneY() - yOffset);
|
taskbar.setLayoutY(event.getSceneY() - yOffset);
|
||||||
|
|
||||||
taskbar.setScaleX(0.9);
|
taskbar.setScaleX(TASKBAR_SCALE);
|
||||||
taskbar.setScaleY(0.9);
|
taskbar.setScaleY(TASKBAR_SCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn die Maus über der Taskleiste losgelassen wird.
|
* Wird aufgerufen, wenn die Maus über der Taskleiste losgelassen wird. Setzt die Skalierung der
|
||||||
* Setzt die Skalierung der Taskleiste wieder auf Normalgröße.
|
* Taskleiste wieder auf Normalgröße.
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event Das Mausereignis
|
||||||
*/
|
*/
|
||||||
@@ -78,8 +84,8 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn der Submit-Button in der Taskleiste gedrückt wird.
|
* Wird aufgerufen, wenn der Submit-Button in der Taskleiste gedrückt wird. Löst die
|
||||||
* Löst die Verarbeitung des Einsatzes aus.
|
* Verarbeitung des Einsatzes aus.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onInputSubmittedAction() {
|
private void onInputSubmittedAction() {
|
||||||
@@ -89,8 +95,8 @@ public class TaskbarController {
|
|||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn der Exit-Button in der Taskleiste gedrückt wird.
|
* Wird aufgerufen, wenn der Exit-Button in der Taskleiste gedrückt wird.
|
||||||
*
|
*
|
||||||
* TODO: Logik implementieren, um zur Lobby zurückzukehren,
|
* <p>TODO: Logik implementieren, um zur Lobby zurückzukehren, ohne die gesamte Anwendung zu
|
||||||
* ohne die gesamte Anwendung zu schließen (kein System.exit/Platform.exit).
|
* schließen (kein System.exit/Platform.exit).
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onExitButtonClick() {
|
private void onExitButtonClick() {
|
||||||
@@ -98,33 +104,32 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verarbeitet den im Textfeld eingegebenen Einsatz.
|
* Verarbeitet den im Textfeld eingegebenen Einsatz. Es werden ausschließlich ganzzahlige Werte
|
||||||
* Es werden ausschließlich ganzzahlige Werte im Bereich von 5 bis 100.000
|
* im Bereich von 5 bis 100.000 Credits akzeptiert, die einem Vielfachen von 5 entsprechen
|
||||||
* Credits akzeptiert, die einem Vielfachen von 5 entsprechen (5er-Schritte).
|
* (5er-Schritte). Der Einsatz wird aktuell nur auf der Konsole ausgegeben.
|
||||||
* Der Einsatz wird aktuell nur auf der Konsole ausgegeben.
|
|
||||||
*/
|
*/
|
||||||
private void processBet() {
|
private void processBet() {
|
||||||
String input = taskbarInput.getText();
|
String input = taskbarInput.getText();
|
||||||
try {
|
try {
|
||||||
int credits = Integer.parseInt(input.trim());
|
int credits = Integer.parseInt(input.trim());
|
||||||
|
|
||||||
if (credits >= 5 && credits <= 100000 && credits % 5 == 0) {
|
if (credits >= MIN_CREDITS && credits <= MAX_CREDITS && credits % CREDIT_STEP == 0) {
|
||||||
// TODO: Credits müssen an die GameEngine gesendet werden
|
// TODO: Credits müssen an die GameEngine gesendet werden
|
||||||
System.out.println("Einsatz gesetzt: " + credits + " Casono Credits");
|
LOGGER.info("Einsatz gesetzt: {} Casono Credits", credits);
|
||||||
taskbarInput.clear();
|
taskbarInput.clear();
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Fehler: Nur 5er-Schritte (5, 10, ... 100.000) erlaubt!");
|
LOGGER.info("Fehler: Nur 5er-Schritte (5, 10, ... 100.000) erlaubt!");
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
System.out.println("Fehler: Bitte nur eine Zahl eingeben!");
|
LOGGER.info("Fehler: Bitte nur eine Zahl eingeben!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Öffnet den integrierten Casono Webbrowser.
|
* Öffnet den integrierten Casono Webbrowser.
|
||||||
*
|
*
|
||||||
* TODO: Ersetze die Start-URL durch die offizielle Projekt-Website (z.B. Tipps & Tricks Seite),
|
* <p>TODO: Ersetze die Start-URL durch die offizielle Projekt-Website (z.B. Tipps & Tricks
|
||||||
* sobald die Inhalte für Strategien und Support bereitstehen.
|
* Seite), sobald die Inhalte für Strategien und Support bereitstehen.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onBrowserButtonClick() {
|
private void onBrowserButtonClick() {
|
||||||
|
|||||||
@@ -1,41 +1,43 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||||
|
|
||||||
/**
|
/** Main UI application for Casono. Loads the main FXML layout and sets up the stage. */
|
||||||
* Main UI application for Casono.
|
import java.io.IOException;
|
||||||
* Loads the main FXML layout and sets up the stage.
|
|
||||||
*/
|
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JavaFX Application class for the Casono main UI.
|
* JavaFX Application class for the Casono main UI.
|
||||||
* <p>
|
*
|
||||||
* Standardkonstruktor für die Anwendung.
|
* <p>Standardkonstruktor für die Anwendung.
|
||||||
*/
|
*/
|
||||||
public class Casinomainui extends Application {
|
public class Casinomainui extends Application {
|
||||||
|
|
||||||
/**
|
/** Standardkonstruktor. */
|
||||||
* Standardkonstruktor.
|
|
||||||
*/
|
|
||||||
public Casinomainui() {
|
public Casinomainui() {
|
||||||
// Standardkonstruktor
|
// Standardkonstruktor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int SCENE_WIDTH = 1200;
|
||||||
|
private static final int SCENE_HEIGHT = 800;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
/**
|
/**
|
||||||
* Starts the JavaFX application and loads the main UI.
|
* Starts the JavaFX application and loads the main UI.
|
||||||
|
*
|
||||||
* @param stage The primary stage for this application.
|
* @param stage The primary stage for this application.
|
||||||
* @throws IOException If loading the FXML fails.
|
* @throws IOException If loading the FXML fails.
|
||||||
*/
|
*/
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ui-structure/Casinomainui.fxml"));
|
FXMLLoader fxmlLoader =
|
||||||
Scene scene = new Scene(fxmlLoader.load(), 1200, 800);
|
new FXMLLoader(getClass().getResource("/ui-structure/Casinomainui.fxml"));
|
||||||
|
Scene scene = new Scene(fxmlLoader.load(), SCENE_WIDTH, SCENE_HEIGHT);
|
||||||
stage.setTitle("Casono");
|
stage.setTitle("Casono");
|
||||||
|
javafx.scene.image.Image icon =
|
||||||
stage.getIcons().add(new javafx.scene.image.Image(getClass().getResource("/images/logoinverted.png").toExternalForm()));
|
new javafx.scene.image.Image(
|
||||||
|
getClass().getResource("/images/logoinverted.png").toExternalForm());
|
||||||
|
stage.getIcons().add(icon);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.setFullScreen(true);
|
stage.setFullScreen(true);
|
||||||
stage.show();
|
stage.show();
|
||||||
@@ -43,6 +45,7 @@ public class Casinomainui extends Application {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for launching the application.
|
* Main entry point for launching the application.
|
||||||
|
*
|
||||||
* @param args Command line arguments
|
* @param args Command line arguments
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
+20
-32
@@ -2,34 +2,27 @@ package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
|||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
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.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/** Controller for the Casono main UI lobby. Handles UI initialization and user actions. */
|
||||||
* Controller for the Casono main UI lobby.
|
|
||||||
* Handles UI initialization and user actions.
|
|
||||||
*/
|
|
||||||
public class CasinomainuiController {
|
public class CasinomainuiController {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(CasinomainuiController.class);
|
||||||
|
|
||||||
@FXML
|
@FXML private AnchorPane rootPane;
|
||||||
private AnchorPane rootPane;
|
@FXML private Label titleLabel;
|
||||||
@FXML
|
@FXML private Label subtitleLabel;
|
||||||
private Label titleLabel;
|
@FXML private ImageView logoView;
|
||||||
@FXML
|
@FXML private Rectangle greenBox;
|
||||||
private Label subtitleLabel;
|
@FXML private Button exitbutton;
|
||||||
@FXML
|
@FXML private VBox casinoTable;
|
||||||
private ImageView logoView;
|
|
||||||
@FXML
|
|
||||||
private Rectangle greenBox;
|
|
||||||
@FXML
|
|
||||||
private Button exitbutton;
|
|
||||||
@FXML
|
|
||||||
private VBox casinoTable;
|
|
||||||
|
|
||||||
private LobbyButtonTranslationManager translationManager;
|
private LobbyButtonTranslationManager translationManager;
|
||||||
private LobbyButtonGridManager gridManager;
|
private LobbyButtonGridManager gridManager;
|
||||||
@@ -39,9 +32,7 @@ public class CasinomainuiController {
|
|||||||
// Default constructor
|
// Default constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Initializes the UI components and sets default values. */
|
||||||
* Initializes the UI components and sets default values.
|
|
||||||
*/
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
titleLabel.setText("Casono");
|
titleLabel.setText("Casono");
|
||||||
@@ -49,37 +40,34 @@ public class CasinomainuiController {
|
|||||||
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
|
||||||
|
|
||||||
translationManager = new LobbyButtonTranslationManager();
|
translationManager = new LobbyButtonTranslationManager();
|
||||||
gridManager = new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
|
gridManager =
|
||||||
|
new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
|
||||||
casinoTable.getChildren().clear();
|
casinoTable.getChildren().clear();
|
||||||
casinoTable.getChildren().add(gridManager.getGridPane());
|
casinoTable.getChildren().add(gridManager.getGridPane());
|
||||||
gridManager.renderLobbyButtons();
|
gridManager.renderLobbyButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Handles the exit button action to close the application. */
|
||||||
* Handles the exit button action to close the application.
|
|
||||||
*/
|
|
||||||
@FXML
|
@FXML
|
||||||
public void handleexitbutton() {
|
public void handleexitbutton() {
|
||||||
Platform.exit();
|
Platform.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Handles creation of a new lobby button. */
|
||||||
* Handles creation of a new lobby button.
|
|
||||||
*/
|
|
||||||
@FXML
|
@FXML
|
||||||
public void handleCreateLobbyButton() {
|
public void handleCreateLobbyButton() {
|
||||||
if (translationManager.isFull()) {
|
if (translationManager.isFull()) {
|
||||||
System.out.println("Grid voll! Keine weiteren Lobbys moeglich.");
|
LOGGER.warn("Grid voll! Keine weiteren Lobbys moeglich.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int buttonId = nextButtonId++;
|
int buttonId = nextButtonId++;
|
||||||
int lobbyId = gridManager.createLobby();
|
int lobbyId = gridManager.createLobby();
|
||||||
try {
|
try {
|
||||||
translationManager.addLobbyButton(buttonId, lobbyId);
|
translationManager.addLobbyButton(buttonId, lobbyId);
|
||||||
System.out.println("ButtonID: " + buttonId + ", LobbyID: " + lobbyId);
|
LOGGER.info("ButtonID: {}, LobbyID: {}", buttonId, lobbyId);
|
||||||
gridManager.renderLobbyButtons();
|
gridManager.renderLobbyButtons();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("Fehler beim Hinzufügen: " + e.getMessage());
|
LOGGER.error("Fehler beim Hinzufügen: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-24
@@ -1,47 +1,57 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the grid for lobby buttons and rendering.
|
* Manages the grid for lobby buttons and rendering. Uses LobbyButtonTranslationManager for mapping
|
||||||
* Uses LobbyButtonTranslationManager for mapping ButtonID to LobbyID.
|
* ButtonID to LobbyID.
|
||||||
*/
|
*/
|
||||||
|
import java.util.Map;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import java.util.Map;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the grid for lobby buttons and rendering.
|
* Manages the grid for lobby buttons and rendering. Uses LobbyButtonTranslationManager for mapping
|
||||||
* Uses LobbyButtonTranslationManager for mapping ButtonID to LobbyID.
|
* ButtonID to LobbyID.
|
||||||
*/
|
*/
|
||||||
public class LobbyButtonGridManager {
|
public class LobbyButtonGridManager {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
||||||
|
|
||||||
/** GridPane for the button grid. */
|
/** GridPane for the button grid. */
|
||||||
private final GridPane gridPane;
|
private final GridPane gridPane;
|
||||||
|
|
||||||
/** Manager for mapping ButtonID to LobbyID. */
|
/** Manager for mapping ButtonID to LobbyID. */
|
||||||
private final LobbyButtonTranslationManager translationManager;
|
private final LobbyButtonTranslationManager translationManager;
|
||||||
|
|
||||||
/** Number of rows in the grid. */
|
/** Number of rows in the grid. */
|
||||||
private final int rows = 2;
|
private static final int ROWS = 2;
|
||||||
|
|
||||||
/** Number of columns in the grid. */
|
/** Number of columns in the grid. */
|
||||||
private final int cols = 4;
|
private static final int COLS = 4;
|
||||||
|
|
||||||
/** Path to the button image. */
|
/** Path to the button image. */
|
||||||
private final String buttonImagePath = "/images/logo.png";
|
private static final String BUTTON_IMAGE_PATH = "/images/logo.png";
|
||||||
|
|
||||||
|
/** Max random lobby id. */
|
||||||
|
private static final int MAX_RANDOM_LOBBY_ID = 10000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the GridManager.
|
* Constructor for the GridManager.
|
||||||
*
|
*
|
||||||
* @param gridPane the GridPane for rendering
|
* @param gridPane the GridPane for rendering
|
||||||
* @param translationManager the manager for mapping ButtonID to LobbyID
|
* @param translationManager the manager for mapping ButtonID to LobbyID
|
||||||
*/
|
*/
|
||||||
public LobbyButtonGridManager(GridPane gridPane, LobbyButtonTranslationManager translationManager) {
|
public LobbyButtonGridManager(
|
||||||
|
GridPane gridPane, LobbyButtonTranslationManager translationManager) {
|
||||||
this.gridPane = gridPane;
|
this.gridPane = gridPane;
|
||||||
this.translationManager = translationManager;
|
this.translationManager = translationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders all lobby buttons in the grid.
|
* Renders all lobby buttons in the grid. Creates a button for each mapping with image and event
|
||||||
* Creates a button for each mapping with image and event handler.
|
* handler.
|
||||||
*/
|
*/
|
||||||
public void renderLobbyButtons() {
|
public void renderLobbyButtons() {
|
||||||
gridPane.getChildren().clear();
|
gridPane.getChildren().clear();
|
||||||
@@ -55,14 +65,17 @@ public class LobbyButtonGridManager {
|
|||||||
int buttonId = entry.getKey();
|
int buttonId = entry.getKey();
|
||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.setId("lobbyBtn-" + buttonId);
|
btn.setId("lobbyBtn-" + buttonId);
|
||||||
btn.setGraphic(new ImageView(new Image(getClass().getResourceAsStream(buttonImagePath))));
|
btn.setGraphic(
|
||||||
btn.setOnAction(e -> {
|
new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH))));
|
||||||
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
|
btn.setOnAction(
|
||||||
if (lobbyId != null)
|
e -> {
|
||||||
joinLobby(lobbyId);
|
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
|
||||||
});
|
if (lobbyId != null) {
|
||||||
int row = index / cols;
|
joinLobby(lobbyId);
|
||||||
int col = index % cols;
|
}
|
||||||
|
});
|
||||||
|
int row = index / COLS;
|
||||||
|
int col = index % COLS;
|
||||||
gridPane.add(btn, col, row);
|
gridPane.add(btn, col, row);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@@ -75,8 +88,8 @@ public class LobbyButtonGridManager {
|
|||||||
*/
|
*/
|
||||||
public int createLobby() {
|
public int createLobby() {
|
||||||
// TODO: Replace with actual lobby creation logic
|
// TODO: Replace with actual lobby creation logic
|
||||||
int lobbyId = (int) (Math.random() * 10000 + 1);
|
int lobbyId = (int) (Math.random() * MAX_RANDOM_LOBBY_ID + 1);
|
||||||
System.out.println("Lobby created: " + lobbyId);
|
LOGGER.info("Lobby created: {}", lobbyId);
|
||||||
return lobbyId;
|
return lobbyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +100,7 @@ public class LobbyButtonGridManager {
|
|||||||
*/
|
*/
|
||||||
public void joinLobby(int lobbyId) {
|
public void joinLobby(int lobbyId) {
|
||||||
// TODO: Replace with actual join logic
|
// TODO: Replace with actual join logic
|
||||||
System.out.println("Joining lobby: " + lobbyId);
|
LOGGER.info("Joining lobby: {}", lobbyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+12
-6
@@ -4,24 +4,24 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher.
|
* Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher. Keine Dateioperationen,
|
||||||
* Keine Dateioperationen, nur Laufzeitdatenstruktur.
|
* nur Laufzeitdatenstruktur.
|
||||||
*/
|
*/
|
||||||
public class LobbyButtonTranslationManager {
|
public class LobbyButtonTranslationManager {
|
||||||
/** Maximale Anzahl an Buttons/Lobbys */
|
/** Maximale Anzahl an Buttons/Lobbys */
|
||||||
private static final int MAX_BUTTONS = 8;
|
private static final int MAX_BUTTONS = 8;
|
||||||
|
|
||||||
/** Zuordnung ButtonID → LobbyID */
|
/** Zuordnung ButtonID → LobbyID */
|
||||||
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/** Konstruktor: initialisiert die Zuordnung leer. */
|
||||||
* Konstruktor: initialisiert die Zuordnung leer.
|
|
||||||
*/
|
|
||||||
public LobbyButtonTranslationManager() {
|
public LobbyButtonTranslationManager() {
|
||||||
// Zuordnung bleibt leer beim Start
|
// Zuordnung bleibt leer beim Start
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht).
|
* Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht).
|
||||||
|
*
|
||||||
* @return true, wenn Grid voll; sonst false
|
* @return true, wenn Grid voll; sonst false
|
||||||
*/
|
*/
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
@@ -30,17 +30,21 @@ public class LobbyButtonTranslationManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fügt eine Zuordnung ButtonID → LobbyID hinzu.
|
* Fügt eine Zuordnung ButtonID → LobbyID hinzu.
|
||||||
|
*
|
||||||
* @param buttonId Die ID des Buttons
|
* @param buttonId Die ID des Buttons
|
||||||
* @param lobbyId Die ID der Lobby
|
* @param lobbyId Die ID der Lobby
|
||||||
* @throws Exception wenn das Grid voll ist
|
* @throws Exception wenn das Grid voll ist
|
||||||
*/
|
*/
|
||||||
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
||||||
if (isFull()) throw new Exception("Grid is full!");
|
if (isFull()) {
|
||||||
|
throw new Exception("Grid is full!");
|
||||||
|
}
|
||||||
buttonIdToLobbyId.put(buttonId, lobbyId);
|
buttonIdToLobbyId.put(buttonId, lobbyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entfernt eine Zuordnung für die gegebene ButtonID.
|
* Entfernt eine Zuordnung für die gegebene ButtonID.
|
||||||
|
*
|
||||||
* @param buttonId Die ID des zu entfernenden Buttons
|
* @param buttonId Die ID des zu entfernenden Buttons
|
||||||
*/
|
*/
|
||||||
public void removeLobbyButton(int buttonId) {
|
public void removeLobbyButton(int buttonId) {
|
||||||
@@ -49,6 +53,7 @@ public class LobbyButtonTranslationManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die LobbyID für eine gegebene ButtonID zurück.
|
* Gibt die LobbyID für eine gegebene ButtonID zurück.
|
||||||
|
*
|
||||||
* @param buttonId Die ButtonID
|
* @param buttonId Die ButtonID
|
||||||
* @return Die zugehoerige LobbyID oder null, falls nicht vorhanden
|
* @return Die zugehoerige LobbyID oder null, falls nicht vorhanden
|
||||||
*/
|
*/
|
||||||
@@ -58,6 +63,7 @@ public class LobbyButtonTranslationManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die gesamte Zuordnung ButtonID → LobbyID zurück.
|
* Gibt die gesamte Zuordnung ButtonID → LobbyID zurück.
|
||||||
|
*
|
||||||
* @return Map aller Zuordnungen
|
* @return Map aller Zuordnungen
|
||||||
*/
|
*/
|
||||||
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
||||||
|
|||||||
+1
-1
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||||
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class LobbyButtonGridManagerTest {
|
class LobbyButtonGridManagerTest {
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
||||||
|
|
||||||
import org.junit.jupiter.api.*;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class LobbyButtonTranslationManagerTest {
|
class LobbyButtonTranslationManagerTest {
|
||||||
|
|||||||
Reference in New Issue
Block a user