Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -17,6 +17,10 @@ The concrete implementations for each command live in `app/commands/` and are wi
|
|||||||
- [Command Infrastructure](./commands-deep-dive.md) -
|
- [Command Infrastructure](./commands-deep-dive.md) -
|
||||||
Technical deep-dive into the `CommandParser`, `CommandParserDispatcher`, `CommandHandler`, `CommandRouter`, `Request`, and the response hierarchy.
|
Technical deep-dive into the `CommandParser`, `CommandParserDispatcher`, `CommandHandler`, `CommandRouter`, `Request`, and the response hierarchy.
|
||||||
|
|
||||||
|
### Protocol document
|
||||||
|
- [Protocol Document](./protocol-document.md) -
|
||||||
|
List of all supported commands by the server, along with descriptions, required pre-execution checks, and an example for both request and response.
|
||||||
|
|
||||||
## Key Concepts
|
## Key Concepts
|
||||||
**Each command is self-contained.**
|
**Each command is self-contained.**
|
||||||
A command's Parser, Request, Handler, and Response all live in the same package under `app/commands/<name>/`.
|
A command's Parser, Request, Handler, and Response all live in the same package under `app/commands/<name>/`.
|
||||||
|
|||||||
@@ -64,11 +64,31 @@ Concrete subclasses add command-specific fields, all set via constructor. Reques
|
|||||||
### `CommandHandler<T extends Request>`
|
### `CommandHandler<T extends Request>`
|
||||||
<img src="../../../images/docs/networking/commands/command_handler.png" alt="Class diagram of the CommandHandler" />
|
<img src="../../../images/docs/networking/commands/command_handler.png" alt="Class diagram of the CommandHandler" />
|
||||||
|
|
||||||
The `CommandHandler` is a single-method interface responsible for executing a typed request.
|
The `CommandHandler` is an abstract base class responsible for executing a typed request.
|
||||||
The handler contains the domain logic: reading from registries and managers, modifying state, and dispatching a response via the `ResponseDispatcher`.
|
The handler contains the domain logic: reading from registries and managers, modifying state, and dispatching a response via the `ResponseDispatcher`.
|
||||||
|
|
||||||
Handlers receive their dependencies (the `ResponseDispatcher`, registries and managers etc.) through constructor injection.
|
Handlers receive their dependencies (the `ResponseDispatcher`, registries and managers etc.) through constructor injection.
|
||||||
This keeps them fully testable without the network layer.
|
They can also register reusable pre-execution checks via `addCheck(...)`. These checks are stored on the handler and are evaluated before `execute(...)` runs.
|
||||||
|
|
||||||
|
When a piece of trivial validation is shared by multiple handlers, it should be extracted into a dedicated `HandlerCheck` instead of being duplicated in each handler.
|
||||||
|
|
||||||
|
### `HandlerCheck`
|
||||||
|
<img src="../../../images/docs/networking/commands/handler_check.png" alt="Class diagram of HandlerCheck and CommandHandlerExecutor" />
|
||||||
|
|
||||||
|
The `HandlerCheck` is a functional interface for reusable pre-execution validation.
|
||||||
|
Its `check(...)` method receives the incoming `Request` and returns an empty `Optional` if the request may continue.
|
||||||
|
If the check fails, it returns an `ErrorResponse` wrapped in the `Optional`, which will be dispatched to the client instead of calling the handler.
|
||||||
|
|
||||||
|
This is the right place for small shared checks such as "is the user logged in?" or other simple preconditions that multiple handlers need.
|
||||||
|
|
||||||
|
### `CommandHandlerExecutor`
|
||||||
|
<img src="../../../images/docs/networking/commands/handler_check_executor.png" alt="Class diagram of HandlerCheck and CommandHandlerExecutor" />
|
||||||
|
|
||||||
|
The `CommandHandlerExecutor` runs all checks registered on a handler before invoking `execute(...)`.
|
||||||
|
If any `HandlerCheck` returns a response, the executor dispatches it immediately and aborts execution.
|
||||||
|
Otherwise, the handler is executed normally.
|
||||||
|
|
||||||
|
This keeps precondition handling separate from the actual domain logic inside the handler.
|
||||||
|
|
||||||
### `CommandRouter`
|
### `CommandRouter`
|
||||||
<img src="../../../images/docs/networking/commands/command_router.png" alt="Class diagram of the CommandRouter" />
|
<img src="../../../images/docs/networking/commands/command_router.png" alt="Class diagram of the CommandRouter" />
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ public class GreetHandler implements CommandHandler<GreetRequest> {
|
|||||||
**Rules for the Handler class:**
|
**Rules for the Handler class:**
|
||||||
|
|
||||||
- Declare all dependencies as `private final` fields, injected through the constructor.
|
- Declare all dependencies as `private final` fields, injected through the constructor.
|
||||||
|
- Use `addCheck(...)` to attach pre-execution checks that should run before `execute(...)`.
|
||||||
|
- If several handlers share the same trivial logic, such as verifying that the user is logged in,
|
||||||
|
extract that logic into a separate `HandlerCheck` and reuse it instead of duplicating the code.
|
||||||
- Always dispatch exactly one response per execution path. Every branch must end with a
|
- Always dispatch exactly one response per execution path. Every branch must end with a
|
||||||
`responseDispatcher.dispatch(...)` call.
|
`responseDispatcher.dispatch(...)` call.
|
||||||
- Use `ErrorResponse` for domain-level failures (e.g. entity not found, precondition not met).
|
- Use `ErrorResponse` for domain-level failures (e.g. entity not found, precondition not met).
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
# Protocol Document
|
||||||
|
This document describes the protocol for client-server communication in our application. It defines the structure of requests and responses, the supported commands along with their request parameters, response formats, and possible errors.
|
||||||
|
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
- [Protocol Document](#protocol-document)
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
|
- [General structure of requests](#general-structure-of-requests)
|
||||||
|
- [Preconditions](#preconditions)
|
||||||
|
- [Parsing](#parsing)
|
||||||
|
- [Error Response](#error-response)
|
||||||
|
- [Command dispatching](#command-dispatching)
|
||||||
|
- [Error Response](#error-response-1)
|
||||||
|
- [Command parsing](#command-parsing)
|
||||||
|
- [Error Response](#error-response-2)
|
||||||
|
- [Pre-execution checks](#pre-execution-checks)
|
||||||
|
- [UserLoggedInCheck](#userloggedincheck)
|
||||||
|
- [Error Response](#error-response-3)
|
||||||
|
- [Commands](#commands)
|
||||||
|
- [PING command](#ping-command)
|
||||||
|
- [Required pre-execution checks](#required-pre-execution-checks)
|
||||||
|
- [Request Parameters](#request-parameters)
|
||||||
|
- [Success Response](#success-response)
|
||||||
|
- [Example Request](#example-request)
|
||||||
|
- [Example Response](#example-response)
|
||||||
|
- [CHECK\_USERNAME command](#check_username-command)
|
||||||
|
- [Required pre-execution checks](#required-pre-execution-checks-1)
|
||||||
|
- [Request Parameters](#request-parameters-1)
|
||||||
|
- [Success Response](#success-response-1)
|
||||||
|
- [Example Request](#example-request-1)
|
||||||
|
- [Example Response](#example-response-1)
|
||||||
|
- [LOGIN command](#login-command)
|
||||||
|
- [Required pre-execution checks](#required-pre-execution-checks-2)
|
||||||
|
- [Request Parameters](#request-parameters-2)
|
||||||
|
- [Success Response](#success-response-2)
|
||||||
|
- [Error Response](#error-response-4)
|
||||||
|
- [Example Request](#example-request-2)
|
||||||
|
- [Example Response](#example-response-2)
|
||||||
|
- [LOGOUT command](#logout-command)
|
||||||
|
- [Required pre-execution checks](#required-pre-execution-checks-3)
|
||||||
|
- [Request Parameters](#request-parameters-3)
|
||||||
|
- [Success Response](#success-response-3)
|
||||||
|
- [Error Response](#error-response-5)
|
||||||
|
- [Example Request](#example-request-3)
|
||||||
|
- [Example Response](#example-response-3)
|
||||||
|
|
||||||
|
<!-- Please see the comments for copy ‚ n' paste ready examples -->
|
||||||
|
|
||||||
|
# General structure of requests
|
||||||
|
As mentioned before, our protocol is based on POP3.
|
||||||
|
Each command is represented as a single line of text, starting with the command name followed by parameters. The server responds with a status line indicating success or failure, followed by the body.
|
||||||
|
|
||||||
|
Requests can have parameters that provide additional information for the command. Parameters are key-value pairs separated by an equal sign (`=`).
|
||||||
|
|
||||||
|
Responses are collections of key-value pairs, containing either a value or another collection, allowing for nested structures.
|
||||||
|
Each collection is ended with the `END` keyword.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Preconditions
|
||||||
|
The serverside pipeline to process incoming requests consists of multiple stages.
|
||||||
|
Each of these stages can yield an error response if the request does not meet the requirements of that stage.
|
||||||
|
|
||||||
|
## Parsing
|
||||||
|
One of these stages is the parsing. It is responsible for parsing the raw request into a structured format that can be easily processed by the command handlers. It validates the syntax of the request as well.
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :-------------- | :---------------------------------------------------------------------------------------------- |
|
||||||
|
| `PARSING_ERROR` | The body of the request contains syntax errors (see message field of response for more details) |
|
||||||
|
|
||||||
|
|
||||||
|
## Command dispatching
|
||||||
|
After the request has been successfully parsed, the next stage is to dispatch the `PrimitiveRequest` to the appropriate `CommandParser`. This is done by the `CommandDispatcherDispatcher`, which uses the command name to determine which parser to use.
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :---------------- | :---------------------------------------------------------------- |
|
||||||
|
| `UNKNOWN_COMMAND` | The command is unknown to this server. No parser has been defined |
|
||||||
|
|
||||||
|
|
||||||
|
## Command parsing
|
||||||
|
Once the `PrimitiveRequest` has been dispatched to the appropriate `CommandParser`, the parser is responsible for parsing the parameters of the request and creating a `Request` that can be executed by the responsible `CommandHandler`.
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :------------------ | :------------------------------------------------------------------------------------------------ |
|
||||||
|
| `MISSING_PARAMETER` | A required parameter is missing from the request (see message field of response for more details) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Pre-execution checks
|
||||||
|
Pre-execution checks are reusable validation steps that can be registered on command handlers.
|
||||||
|
They are implemented as `HandlerCheck` instances and are executed by the `CommandHandlerExecutor` before the handler's main logic is invoked.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Name of the check
|
||||||
|
Description of the check, what it does and when it should be used.
|
||||||
|
|
||||||
|
### Response if not met
|
||||||
|
| Code | Description |
|
||||||
|
| :----------- | :----------------------- |
|
||||||
|
| `ERROR_CODE` | Description of the error |
|
||||||
|
-->
|
||||||
|
|
||||||
|
## UserLoggedInCheck
|
||||||
|
The `UserLoggedInCheck` is a common pre-execution check that verifies whether the user is logged in (i.e. has a user associated with his session).
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :------------------- | :------------------------ |
|
||||||
|
| `USER_NOT_LOGGED_IN` | The user is not logged in |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
Commands are the core of our protocol, representing the various actions that clients can request from the server. Each command has a unique name and may require specific parameters in addition to pre-execution checks.
|
||||||
|
The server processes these commands and responds accordingly.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Name of the command
|
||||||
|
Description of the command, what it does, and when it should be used.
|
||||||
|
|
||||||
|
### Required pre-execution checks
|
||||||
|
- [`Check1`](#check1)
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
| Parameter Name | Type | Optional | Description |
|
||||||
|
| :------------- | :----- | :---------------------------------- | :-------------------- |
|
||||||
|
| `param1` | `type` | If the parameter is optional or not | Description of param1 |
|
||||||
|
|
||||||
|
### Success Response
|
||||||
|
| Field | Type | Description |
|
||||||
|
| :------- | :------------------ | :-------------------- |
|
||||||
|
| `field1` | `type` | Description of field1 |
|
||||||
|
| `field2` | `Collection<type2>` | |
|
||||||
|
| `field3` | `Enum<type3>` | Description of field3 |
|
||||||
|
|
||||||
|
| Fields of `type2` | Type | Description |
|
||||||
|
| :---------------- | :----- | :-------------------- |
|
||||||
|
| `field1` | `type` | Description of field1 |
|
||||||
|
|
||||||
|
| Members of `type` | Description |
|
||||||
|
| :---------------- | :--------------------- |
|
||||||
|
| `MEMBER1` | Description of member1 |
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :----------- | :----------------------- |
|
||||||
|
| `ERROR_CODE` | Description of the error |
|
||||||
|
|
||||||
|
### Example Request
|
||||||
|
```
|
||||||
|
COMMAND_NAME PARAM1='value1' PARAM2='value2'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
```
|
||||||
|
+OK
|
||||||
|
KEY1=VALUE1
|
||||||
|
FIELDS
|
||||||
|
FIELD
|
||||||
|
NESTED_KEY=NESTED_VALUE
|
||||||
|
END
|
||||||
|
KEY2=VALUE2
|
||||||
|
END
|
||||||
|
```
|
||||||
|
-->
|
||||||
|
|
||||||
|
## PING command
|
||||||
|
The `PING` command is a simple command that can be used to check if the server is responsive.
|
||||||
|
|
||||||
|
### Required pre-execution checks
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
No parameters.
|
||||||
|
|
||||||
|
### Success Response
|
||||||
|
No response fields.
|
||||||
|
|
||||||
|
### Example Request
|
||||||
|
```
|
||||||
|
PING
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
```
|
||||||
|
+OK
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## CHECK_USERNAME command
|
||||||
|
The `CHECK_USERNAME` command is used to check if a username is already taken by another user. Additional users can still log in with the same username, but their name will be substituted with a suffix.
|
||||||
|
|
||||||
|
### Required pre-execution checks
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
| Parameter Name | Type | Optional | Description |
|
||||||
|
| :------------- | :------- | :------- | :------------------------------------- |
|
||||||
|
| `USERNAME` | `String` | no | The username to check for availability |
|
||||||
|
|
||||||
|
### Success Response
|
||||||
|
| Field | Type | Description |
|
||||||
|
| :------- | :--------------------------- | :---------------------------------------------------------------------- |
|
||||||
|
| `STATUS` | `Enum<UsernameAvailability>` | Member of enum indicating if the username is available or already taken |
|
||||||
|
|
||||||
|
| Members of `UsernameAvailability` | Description |
|
||||||
|
| :-------------------------------- | :------------------------- |
|
||||||
|
| `FREE` | Username is available |
|
||||||
|
| `TAKEN` | Username is already in use |
|
||||||
|
|
||||||
|
### Example Request
|
||||||
|
```
|
||||||
|
CHECK_USERNAME USERNAME='Lars'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
```
|
||||||
|
+OK
|
||||||
|
STATUS=FREE
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
## LOGIN command
|
||||||
|
The `LOGIN` command is used to log in a user with a specified username. If the username is already taken by another user, the server will append a suffix to the username to make it unique.
|
||||||
|
|
||||||
|
### Required pre-execution checks
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
| Parameter Name | Type | Optional | Description |
|
||||||
|
| :------------- | :------- | :------- | :----------------------------------- |
|
||||||
|
| `USERNAME` | `String` | no | The username to create the user with |
|
||||||
|
|
||||||
|
### Success Response
|
||||||
|
| Field | Type | Description |
|
||||||
|
| :--------- | :---------------------------------------------------------------------- | :-------------------------------------------------------------------- |
|
||||||
|
| `USERNAME` | `String` | Username of the newly created user, can differ from the requested one |
|
||||||
|
| `ID` | [`UUID`](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html) | The ID of the created user |
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :------------------ | :----------------------------------------------------------------------------- |
|
||||||
|
| `ALREADY_LOGGED_IN` | The session is already associated with a user, logging in again is prohibited. |
|
||||||
|
|
||||||
|
### Example Request
|
||||||
|
```
|
||||||
|
LOGIN USERNAME='Lars'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
```
|
||||||
|
+OK
|
||||||
|
USERNAME='Lars_1234'
|
||||||
|
ID=e47a671e-2b2a-42df-bb82-953fe2ebd307
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## LOGOUT command
|
||||||
|
Description of the command, what it does, and when it should be used.
|
||||||
|
|
||||||
|
### Required pre-execution checks
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
No parameters.
|
||||||
|
|
||||||
|
### Success Response
|
||||||
|
No response fields.
|
||||||
|
|
||||||
|
### Error Response
|
||||||
|
| Code | Description |
|
||||||
|
| :------------------- | :--------------------------------------------------------------- |
|
||||||
|
| `NO_USER_ASSOCIATED` | The session has no user associated, logging out is not possible. |
|
||||||
|
|
||||||
|
### Example Request
|
||||||
|
```
|
||||||
|
LOGOUT
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Response
|
||||||
|
```
|
||||||
|
+OK
|
||||||
|
END
|
||||||
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,8 @@
|
|||||||
|
@startuml
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
|
||||||
|
interface HandlerCheck {
|
||||||
|
+ check(request: Request): Optional<Response>
|
||||||
|
}
|
||||||
|
|
||||||
|
@enduml
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,31 @@
|
|||||||
|
@startuml
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
|
||||||
|
class CommandHandlerExecutor {
|
||||||
|
- responseDispatcher: ResponseDispatcher
|
||||||
|
+ CommandHandlerExecutor(responseDispatcher: ResponseDispatcher)
|
||||||
|
+ execute(handler: CommandHandler<Request>, request: Request): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HandlerCheck {
|
||||||
|
+ check(request: Request): Optional<Response>
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CommandHandler<T extends Request> {
|
||||||
|
- checks: List<HandlerCheck>
|
||||||
|
# addCheck(check: HandlerCheck): void
|
||||||
|
+ getChecks(): List<HandlerCheck>
|
||||||
|
+ execute(request: T): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponseDispatcher
|
||||||
|
class Request
|
||||||
|
class Response
|
||||||
|
|
||||||
|
CommandHandlerExecutor --> CommandHandler : executes
|
||||||
|
CommandHandler "1" o-- "0..*" HandlerCheck : registered checks
|
||||||
|
CommandHandlerExecutor --> HandlerCheck : evaluates
|
||||||
|
HandlerCheck ..> Request : inspects
|
||||||
|
HandlerCheck ..> Response : returns failure response
|
||||||
|
CommandHandlerExecutor --> ResponseDispatcher : dispatches failures
|
||||||
|
@enduml
|
||||||
+10
-10
@@ -5,17 +5,17 @@ import javafx.scene.control.Label;
|
|||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller für die Casino-Spielfläche.
|
* Controller for the casino gaming area.
|
||||||
*
|
*
|
||||||
* <p>Verantwortlich für: - die Darstellung des Pokertisches und der Spieleroberfläche, - die
|
* <p>Responsible for: - the display of the poker table and the player interface, - the processing
|
||||||
* Verarbeitung von Benutzereingaben, - die Schnittstelle zur GameEngine und zum Netzwerkprotokoll.
|
* of user input, - the interface to the game engine and the network protocol.
|
||||||
*
|
*
|
||||||
* <p>Hinweise: - Die Methode `onTableClick()` dient aktuell nur als Test-Logik. Sie ist ggf. nicht
|
* <p>Notes: - The `onTableClick()` method currently serves only as test logic. It may no longer be
|
||||||
* mehr funktionsfähig und wird zukünftig durch die finale Spielinteraktion ersetzt.
|
* functional and will be replaced by the final game interaction in the future.
|
||||||
*/
|
*/
|
||||||
public class CasinoGameController {
|
public class CasinoGameController {
|
||||||
|
|
||||||
/** Standardkonstruktor. Wird von FXML verwendet. */
|
/** Standard constructor. Used by FXML. */
|
||||||
public CasinoGameController() {
|
public CasinoGameController() {
|
||||||
// default constructor for FXML
|
// default constructor for FXML
|
||||||
}
|
}
|
||||||
@@ -23,13 +23,13 @@ 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,
|
// TODO: Test logic: will be replaced by real game interactions,
|
||||||
// sobald die GameEngine fertig ist
|
// once the game engine is finished
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporäre Test-Methode, die bei Klick auf den Tisch eine Platzhalteraktion ausführt.
|
* Temporary test method that performs a placeholder action when the table is clicked.
|
||||||
*
|
*
|
||||||
* <p>Wird in der finalen Implementierung durch die Spiel-Logik ersetzt.
|
* <p>In the final implementation, this will be replaced by the game logic.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
public void onTableClick() {
|
public void onTableClick() {
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ import javafx.scene.Scene;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hauptklasse für das Casino-Spiel-UI.
|
* Main class for the Casono Game UI.
|
||||||
*
|
*
|
||||||
* <p>Startet die JavaFX-Anwendung, lädt die grafische Oberfläche aus der FXML-Datei und
|
* <p>Starts the JavaFX application, loads the graphical user interface from the FXML file, and
|
||||||
* initialisiert die Haupt-Stage für das Spiel.
|
* initializes the main stage for the game.
|
||||||
*
|
*
|
||||||
* <p>Aufgaben: - Lädt die FXML-Oberfläche "/ui-structure/Casinogameui.fxml". - Lädt das
|
* <p>Tasks: - Loads the FXML interface "/ui-structure/Casinogameui.fxml". - Loads the application
|
||||||
* Anwendungs-Icon aus "/images/logoinverted.png". - Startet die Anwendung im Vollbildmodus.
|
* icon from "/images/logoinverted.png". - Starts the application in full-screen mode.
|
||||||
*/
|
*/
|
||||||
public class CasinoGameUI extends Application {
|
public class CasinoGameUI extends Application {
|
||||||
|
|
||||||
/** Standardkonstruktor. */
|
/** default constructor */
|
||||||
public CasinoGameUI() {
|
public CasinoGameUI() {
|
||||||
// default no-arg constructor
|
// default no-arg constructor
|
||||||
}
|
}
|
||||||
@@ -26,10 +26,10 @@ public class CasinoGameUI extends Application {
|
|||||||
private static final int DEFAULT_HEIGHT = 800;
|
private static final int DEFAULT_HEIGHT = 800;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Startet die Haupt-Stage der Anwendung.
|
* Starts the main stage of the application.
|
||||||
*
|
*
|
||||||
* @param stage Die vom System bereitgestellte Haupt-Stage.
|
* @param stage The main stage provided by the system.
|
||||||
* @throws IOException Wenn die FXML-Datei oder Ressourcen nicht geladen werden können.
|
* @throws IOException If the FXML file or resources cannot be loaded.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
@@ -46,9 +46,9 @@ public class CasinoGameUI extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Startpunkt der Anwendung.
|
* Starting point of the application.
|
||||||
*
|
*
|
||||||
* @param args Befehlszeilenargumente.
|
* @param args Command line arguments.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
launch();
|
launch();
|
||||||
|
|||||||
+78
-80
@@ -31,28 +31,27 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimenteller integrierter Browser für Casono.
|
* Experimental embedded browser for Casono.
|
||||||
*
|
*
|
||||||
* <p>Diese Klasse implementiert einen einfachen eingebetteten Webbrowser auf Basis von {@link
|
* <p>This class implements a simple embedded web browser based on {@link javafx.scene.web.WebView}.
|
||||||
* javafx.scene.web.WebView}. Der Browser dient primär als Hilfswerkzeug innerhalb des Spiels, um
|
* The browser primarily serves as a utility within the game to display external content such as web
|
||||||
* externe Inhalte wie Webseiten oder Videos anzuzeigen.
|
* pages or videos.
|
||||||
*
|
*
|
||||||
* <p>Status Der Browser befindet sich derzeit in einer experimentellen Phase. Einige
|
* <p>Status The browser is currently in an experimental phase. Some security mechanisms are based
|
||||||
* Sicherheitsmechanismen basieren auf experimentellen KI-gestützten Empfehlungen und können sich in
|
* on experimental AI-driven recommendations and may change in future versions.
|
||||||
* zukünftigen Versionen noch ändern.
|
|
||||||
*
|
*
|
||||||
* <p>Zweck Der Browser wird aktuell experimentell genutzt, um: Pokerregeln direkt im Spiel zu
|
* <p>Purpose The browser is currently being used experimentally to: Explain poker rules directly
|
||||||
* erklären Hilfeseiten oder Dokumentationen anzuzeigen Videos (z.B. Tutorials oder Erklärungen)
|
* within the game Display help pages or documentation Play videos (e.g., tutorials or explanations)
|
||||||
* über Plattformen wie YouTube abzuspielen
|
* via platforms such as YouTube
|
||||||
*
|
*
|
||||||
* <p>Sicherheitsmechanismen Da externe Webseiten geladen werden können, wurden einige grundlegende
|
* <p>Security Mechanisms Since external websites can be loaded, some basic protective measures have
|
||||||
* Schutzmaßnahmen integriert: - HTTPS-Zwang für Webseiten - Whitelist für bekannte Domains -
|
* been integrated: - Mandatory HTTPS for websites - Whitelist for known domains - Warning for
|
||||||
* Warnung bei unbekannten Webseiten - JavaScript standardmäßig deaktiviert (man kann es jedoch für
|
* unknown websites - JavaScript disabled by default (but can be enabled for Google, etc.) - Pop-up
|
||||||
* Google etc. einschalten) - Popup-Blocker - Automatische Cookie-Löschung beim Schließen
|
* blocker - Automatic cookie deletion upon closing
|
||||||
*/
|
*/
|
||||||
public class CasinoBrowserController {
|
public class CasinoBrowserController {
|
||||||
|
|
||||||
/** Standardkonstruktor. Initialisiert den CasinoBrowserController. */
|
/** Default constructor. Initializes the CasinoBrowserController. */
|
||||||
public CasinoBrowserController() {
|
public CasinoBrowserController() {
|
||||||
// Intentionally left blank; controller initialization is FXML-driven.
|
// Intentionally left blank; controller initialization is FXML-driven.
|
||||||
}
|
}
|
||||||
@@ -101,7 +100,7 @@ public class CasinoBrowserController {
|
|||||||
|
|
||||||
private static boolean javascriptEnabled = false;
|
private static boolean javascriptEnabled = false;
|
||||||
|
|
||||||
/** Löscht alle gespeicherten Cookies der aktuellen Browser-Sitzung. */
|
/** Deletes all cookies stored during the current browser session. */
|
||||||
private static void clearCookies() {
|
private static void clearCookies() {
|
||||||
try {
|
try {
|
||||||
COOKIE_MANAGER.getCookieStore().removeAll();
|
COOKIE_MANAGER.getCookieStore().removeAll();
|
||||||
@@ -110,12 +109,12 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Öffnet ein neues Browserfenster und lädt eine angegebene Webseite.
|
* Opens a new browser window and loads a specified webpage.
|
||||||
*
|
*
|
||||||
* <p>Falls die Webseite nicht zur Liste vertrauenswürdiger Domains gehört, wird der Benutzer
|
* <p>If the webpage is not on the list of trusted domains, the user will be asked whether the
|
||||||
* gefragt, ob die Seite dennoch geladen werden soll.
|
* page should be loaded anyway.
|
||||||
*
|
*
|
||||||
* @param url die Startadresse der Webseite, die geladen werden soll
|
* @param url the URL of the webpage to be loaded
|
||||||
*/
|
*/
|
||||||
public static void open(String url) {
|
public static void open(String url) {
|
||||||
Platform.runLater(
|
Platform.runLater(
|
||||||
@@ -182,10 +181,10 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebView Container
|
* WebView container
|
||||||
*
|
*
|
||||||
* @param webView WebView Inhalt
|
* @param webView WebView content
|
||||||
* @return StackPane Container
|
* @return StackPane container
|
||||||
*/
|
*/
|
||||||
public static StackPane createWebContainer(WebView webView) {
|
public static StackPane createWebContainer(WebView webView) {
|
||||||
StackPane webContainer = new StackPane(webView);
|
StackPane webContainer = new StackPane(webView);
|
||||||
@@ -198,7 +197,7 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* Popup Blocker
|
* Popup Blocker
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
*/
|
*/
|
||||||
public static void configurePopupBlocker(WebEngine engine) {
|
public static void configurePopupBlocker(WebEngine engine) {
|
||||||
engine.setCreatePopupHandler(
|
engine.setCreatePopupHandler(
|
||||||
@@ -212,8 +211,8 @@ public class CasinoBrowserController {
|
|||||||
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
Image logo = new Image(stream);
|
Image logo = new Image(stream);
|
||||||
|
|
||||||
// Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
|
// Variable ‘streamM’ abbreviated to comply with the 100-character
|
||||||
// einzuhalten
|
// limit (LineLength)
|
||||||
var streamM =
|
var streamM =
|
||||||
CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
||||||
Image logomain = new Image(streamM);
|
Image logomain = new Image(streamM);
|
||||||
@@ -232,7 +231,7 @@ public class CasinoBrowserController {
|
|||||||
alert.setGraphic(logoView);
|
alert.setGraphic(logoView);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Logo konnte nicht geladen werden");
|
LOGGER.error("The logo could not be loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
@@ -241,9 +240,9 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logos laden
|
* Load logos
|
||||||
*
|
*
|
||||||
* @param stage Fenster Stage
|
* @param stage Window Stage
|
||||||
* @return ImageView Logo
|
* @return ImageView Logo
|
||||||
*/
|
*/
|
||||||
private static ImageView loadLogos(Stage stage) {
|
private static ImageView loadLogos(Stage stage) {
|
||||||
@@ -253,8 +252,7 @@ public class CasinoBrowserController {
|
|||||||
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
Image logo = new Image(stream);
|
Image logo = new Image(stream);
|
||||||
|
|
||||||
// Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
|
// Variable ‘streamM’ abbreviated to comply with the 100-character limit (LineLength)
|
||||||
// einzuhalten
|
|
||||||
var streamM = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
var streamM = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
|
||||||
Image logomain = new Image(streamM);
|
Image logomain = new Image(streamM);
|
||||||
|
|
||||||
@@ -268,17 +266,17 @@ public class CasinoBrowserController {
|
|||||||
browserLogo.setPreserveRatio(true);
|
browserLogo.setPreserveRatio(true);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Logo konnte nicht geladen werden");
|
LOGGER.error("The logo could not be loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
return browserLogo;
|
return browserLogo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL Feld
|
* URL field
|
||||||
*
|
*
|
||||||
* @param url Start URL
|
* @param url Start URL
|
||||||
* @return TextField Eingabe
|
* @return TextField input
|
||||||
*/
|
*/
|
||||||
public static TextField createUrlField(String url) {
|
public static TextField createUrlField(String url) {
|
||||||
TextField urlField = new TextField(url);
|
TextField urlField = new TextField(url);
|
||||||
@@ -289,12 +287,12 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sicherheits Label
|
* Security Label
|
||||||
*
|
*
|
||||||
* @return Label Anzeige
|
* @return Label display
|
||||||
*/
|
*/
|
||||||
public static Label createSecurityLabel() {
|
public static Label createSecurityLabel() {
|
||||||
Label securityLabel = new Label("SICHER");
|
Label securityLabel = new Label("SAFE");
|
||||||
securityLabel.getStyleClass().add("security-label");
|
securityLabel.getStyleClass().add("security-label");
|
||||||
|
|
||||||
return securityLabel;
|
return securityLabel;
|
||||||
@@ -303,8 +301,8 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* JS Toggle
|
* JS Toggle
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
* @return Button Umschalten
|
* @return Toggle button
|
||||||
*/
|
*/
|
||||||
public static Button createJsToggle(WebEngine engine) {
|
public static Button createJsToggle(WebEngine engine) {
|
||||||
Button jsToggle = new Button("JS EINSCHALTEN");
|
Button jsToggle = new Button("JS EINSCHALTEN");
|
||||||
@@ -316,7 +314,7 @@ public class CasinoBrowserController {
|
|||||||
engine.setJavaScriptEnabled(javascriptEnabled);
|
engine.setJavaScriptEnabled(javascriptEnabled);
|
||||||
|
|
||||||
if (javascriptEnabled) {
|
if (javascriptEnabled) {
|
||||||
jsToggle.setText("JS AUSSCHALTEN");
|
jsToggle.setText("JS TURN OFF");
|
||||||
jsToggle.getStyleClass().removeAll("red-button");
|
jsToggle.getStyleClass().removeAll("red-button");
|
||||||
jsToggle.getStyleClass().add("yellow-button");
|
jsToggle.getStyleClass().add("yellow-button");
|
||||||
|
|
||||||
@@ -331,10 +329,10 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zurück Button
|
* Back Button
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
* @return Button Zurück
|
* @return Back button
|
||||||
*/
|
*/
|
||||||
public static Button createBackButton(WebEngine engine) {
|
public static Button createBackButton(WebEngine engine) {
|
||||||
Button backBtn = new Button("<");
|
Button backBtn = new Button("<");
|
||||||
@@ -350,10 +348,10 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vorwärts Button
|
* Forward Button
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
* @return Button Vorwärts
|
* @return Forward button
|
||||||
*/
|
*/
|
||||||
public static Button createForwardButton(WebEngine engine) {
|
public static Button createForwardButton(WebEngine engine) {
|
||||||
Button fwdBtn = new Button(">");
|
Button fwdBtn = new Button(">");
|
||||||
@@ -373,8 +371,8 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* Reload Button
|
* Reload Button
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
* @return Button Reload
|
* @return Reload button
|
||||||
*/
|
*/
|
||||||
public static Button createReloadButton(WebEngine engine) {
|
public static Button createReloadButton(WebEngine engine) {
|
||||||
Button reloadBtn = new Button("⟳");
|
Button reloadBtn = new Button("⟳");
|
||||||
@@ -386,9 +384,9 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* Close Button
|
* Close Button
|
||||||
*
|
*
|
||||||
* @param stage Fenster Stage
|
* @param stage Window stage
|
||||||
* @param webView WebView Inhalt
|
* @param webView WebView content
|
||||||
* @return Button Schließen
|
* @return Close button
|
||||||
*/
|
*/
|
||||||
public static Button createCloseButton(Stage stage, WebView webView) {
|
public static Button createCloseButton(Stage stage, WebView webView) {
|
||||||
Button closeBtn = new Button("X");
|
Button closeBtn = new Button("X");
|
||||||
@@ -406,9 +404,9 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* URL Events
|
* URL Events
|
||||||
*
|
*
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
* @param urlField URL Textfeld
|
* @param urlField URL text field
|
||||||
* @param securityLabel Sicherheits Label
|
* @param securityLabel Security label
|
||||||
*/
|
*/
|
||||||
public static void configureUrlEvents(
|
public static void configureUrlEvents(
|
||||||
WebEngine engine, TextField urlField, Label securityLabel) {
|
WebEngine engine, TextField urlField, Label securityLabel) {
|
||||||
@@ -417,11 +415,11 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scene erstellen
|
* Create a scene
|
||||||
*
|
*
|
||||||
* @param taskbar Taskbar HBox
|
* @param taskbar Taskbar HBox
|
||||||
* @param webContainer Web Container
|
* @param webContainer Web container
|
||||||
* @return Scene Fenster
|
* @return Scene window
|
||||||
*/
|
*/
|
||||||
public static Scene createScene(HBox taskbar, StackPane webContainer) {
|
public static Scene createScene(HBox taskbar, StackPane webContainer) {
|
||||||
VBox root = new VBox(VBOX_SPACING, taskbar, webContainer);
|
VBox root = new VBox(VBOX_SPACING, taskbar, webContainer);
|
||||||
@@ -442,8 +440,8 @@ public class CasinoBrowserController {
|
|||||||
/**
|
/**
|
||||||
* Key Events
|
* Key Events
|
||||||
*
|
*
|
||||||
* @param scene Scene Fenster
|
* @param scene Scene window
|
||||||
* @param engine WebEngine nutzen
|
* @param engine Use WebEngine
|
||||||
*/
|
*/
|
||||||
public static void configureKeyEvents(Scene scene, WebEngine engine) {
|
public static void configureKeyEvents(Scene scene, WebEngine engine) {
|
||||||
scene.setOnKeyPressed(
|
scene.setOnKeyPressed(
|
||||||
@@ -455,18 +453,18 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt eine URL in den Browser, nachdem grundlegende Sicherheitsprüfungen durchgeführt wurden.
|
* Loads a URL in the browser after performing basic security checks.
|
||||||
*
|
*
|
||||||
* <p>Vor dem Laden einer Seite werden folgende Prüfungen durchgeführt: - Überprüfung des
|
* <p>Before loading a page, the following checks are performed: - Verification of the protocol
|
||||||
* Protokolls (nur HTTPS erlaubt) - Überprüfung der Domain gegen eine Whitelist - Schutz vor
|
* (only HTTPS allowed) - Verification of the domain against a whitelist - Protection against
|
||||||
* Domain-Spoofing (Domain-Vortäuschung)
|
* domain spoofing
|
||||||
*
|
*
|
||||||
* <p>Falls eine Domain nicht als vertrauenswürdig eingestuft wird, muss der Benutzer
|
* <p>If a domain is not classified as trustworthy, the user must confirm that the page may
|
||||||
* bestätigen, dass die Seite dennoch geöffnet werden darf.
|
* still be opened.
|
||||||
*
|
*
|
||||||
* @param engine der WebEngine-Renderer des Browsers
|
* @param engine the browser's WebEngine renderer
|
||||||
* @param url die zu ladende Webadresse
|
* @param url the web address to be loaded
|
||||||
* @param securityLabel Label zur Anzeige des aktuellen Sicherheitsstatus
|
* @param securityLabel label for displaying the current security status
|
||||||
*/
|
*/
|
||||||
private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) {
|
private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) {
|
||||||
try {
|
try {
|
||||||
@@ -476,9 +474,9 @@ public class CasinoBrowserController {
|
|||||||
|
|
||||||
URI uri = new URI(url);
|
URI uri = new URI(url);
|
||||||
|
|
||||||
// HTTPS Pflicht
|
// HTTPS required
|
||||||
if (!"https".equalsIgnoreCase(uri.getScheme())) {
|
if (!"https".equalsIgnoreCase(uri.getScheme())) {
|
||||||
securityLabel.setText("BLOCKIERT");
|
securityLabel.setText("BLOCKED");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,19 +486,19 @@ public class CasinoBrowserController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sicherer Domain Check
|
// Secure Domain Check
|
||||||
boolean trusted =
|
boolean trusted =
|
||||||
TRUSTED_DOMAINS.stream()
|
TRUSTED_DOMAINS.stream()
|
||||||
.anyMatch(domain -> host.equals(domain) || host.endsWith("." + domain));
|
.anyMatch(domain -> host.equals(domain) || host.endsWith("." + domain));
|
||||||
|
|
||||||
if (!trusted) {
|
if (!trusted) {
|
||||||
if (!showUnknownWebsiteAlert(host)) {
|
if (!showUnknownWebsiteAlert(host)) {
|
||||||
securityLabel.setText("BLOCKIERT");
|
securityLabel.setText("BLOCKED");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
securityLabel.setText("UNBEKANNT");
|
securityLabel.setText("UNBEKANNT");
|
||||||
} else {
|
} else {
|
||||||
securityLabel.setText("SICHER");
|
securityLabel.setText("SAFE");
|
||||||
}
|
}
|
||||||
engine.load(uri.toString());
|
engine.load(uri.toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -509,20 +507,20 @@ public class CasinoBrowserController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeigt Warnung an.
|
* Displays a warning.
|
||||||
*
|
*
|
||||||
* @param host Website Host
|
* @param host Website host
|
||||||
* @return OK gedrückt
|
* @return OK pressed
|
||||||
*/
|
*/
|
||||||
private static boolean showUnknownWebsiteAlert(String host) {
|
private static boolean showUnknownWebsiteAlert(String host) {
|
||||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
|
||||||
alert.setTitle("Unbekannte Website");
|
alert.setTitle("Unknown website");
|
||||||
alert.setHeaderText("Diese Website ist nicht bekannt");
|
alert.setHeaderText("This website is unknown");
|
||||||
String content =
|
String content =
|
||||||
host
|
host
|
||||||
+ "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\n"
|
+ "\n\nThis site has not been verified by the Casono browser.\n"
|
||||||
+ "Möchten Sie sie trotzdem öffnen?";
|
+ "Do you still want to open it?";
|
||||||
alert.setContentText(content);
|
alert.setContentText(content);
|
||||||
|
|
||||||
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
|
||||||
|
|||||||
+28
-29
@@ -11,14 +11,14 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller für die interaktive Taskleiste innerhalb der Poker-UI.
|
* Controller for the interactive taskbar within the poker UI.
|
||||||
*
|
*
|
||||||
* <p>Verantwortlich für: - Drag-and-Drop-Verschieben der Taskleiste, - Eingabe und Verwaltung von
|
* <p>Responsible for: - Drag-and-drop movement of the taskbar, - Input and management of game
|
||||||
* Spieleinsätzen, - Steuerung allgemeiner Menüfunktionen wie Exit.
|
* stakes, - Control of general menu functions such as Exit.
|
||||||
*/
|
*/
|
||||||
public class TaskbarController {
|
public class TaskbarController {
|
||||||
|
|
||||||
/** Standardkonstruktor. Wird von FXML verwendet. */
|
/** Standard constructor. Used by FXML. */
|
||||||
public TaskbarController() {
|
public TaskbarController() {
|
||||||
// default constructor for FXML
|
// default constructor for FXML
|
||||||
}
|
}
|
||||||
@@ -36,10 +36,10 @@ public class TaskbarController {
|
|||||||
private static final int CREDIT_STEP = 5;
|
private static final int CREDIT_STEP = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn die Taskleiste mit der Maus gedrückt wird. Speichert die relative
|
* Called when the taskbar is clicked with the mouse. Saves the relative position for later,
|
||||||
* Position, um später korrekt zu verschieben.
|
* correct repositioning.
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event The mouse event
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onTaskbarPressed(MouseEvent event) {
|
private void onTaskbarPressed(MouseEvent event) {
|
||||||
@@ -48,11 +48,10 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, während die Taskleiste mit der Maus gezogen wird. Aktualisiert die Position
|
* Called while dragging the taskbar with the mouse. Updates the position and slightly scales
|
||||||
* und skaliert die Taskleiste leicht zur visuellen Rückmeldung.
|
* the taskbar for visual feedback.
|
||||||
*
|
*
|
||||||
* <p>TODO: Es muss noch gefixt werden, dass die Taskleiste nicht aus dem Fenster verschwinden
|
* <p>TODO: It still needs to be fixed that the taskbar cannot disappear out of the window.
|
||||||
* kann.
|
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event Das Mausereignis
|
||||||
*/
|
*/
|
||||||
@@ -66,10 +65,10 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn die Maus über der Taskleiste losgelassen wird. Setzt die Skalierung der
|
* Called when the mouse cursor is released over the taskbar. Resets the taskbar scaling to
|
||||||
* Taskleiste wieder auf Normalgröße.
|
* normal size.
|
||||||
*
|
*
|
||||||
* @param event Das Mausereignis
|
* @param event The mouse event
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onTaskbarReleased(MouseEvent event) {
|
private void onTaskbarReleased(MouseEvent event) {
|
||||||
@@ -78,9 +77,9 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn im Textfeld die Enter-Taste gedrückt wird.
|
* Called up when the Enter key is pressed in the text field.
|
||||||
*
|
*
|
||||||
* @param event Das Tastaturereignis
|
* @param event The keyboard event
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onInputSubmitted(KeyEvent event) {
|
private void onInputSubmitted(KeyEvent event) {
|
||||||
@@ -90,8 +89,8 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen, wenn der Submit-Button in der Taskleiste gedrückt wird. Löst die
|
* Called when the submit button in the taskbar is pressed. Triggers the processing of the
|
||||||
* Verarbeitung des Einsatzes aus.
|
* deployment.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onInputSubmittedAction() {
|
private void onInputSubmittedAction() {
|
||||||
@@ -106,7 +105,7 @@ public class TaskbarController {
|
|||||||
javafx.stage.Stage currentStage =
|
javafx.stage.Stage currentStage =
|
||||||
(javafx.stage.Stage) taskbar.getScene().getWindow();
|
(javafx.stage.Stage) taskbar.getScene().getWindow();
|
||||||
currentStage.close();
|
currentStage.close();
|
||||||
// Lobby-UI starten
|
// Start lobby UI
|
||||||
try {
|
try {
|
||||||
new Casinomainui().start(new javafx.stage.Stage());
|
new Casinomainui().start(new javafx.stage.Stage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -116,9 +115,9 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verarbeitet den im Textfeld eingegebenen Einsatz. Es werden ausschließlich ganzzahlige Werte
|
* Processes the stake entered in the text field. Only integer values between 5 and 100,000
|
||||||
* im Bereich von 5 bis 100.000 Credits akzeptiert, die einem Vielfachen von 5 entsprechen
|
* credits are accepted, in multiples of 5 (in increments of 5). The stake is currently only
|
||||||
* (5er-Schritte). Der Einsatz wird aktuell nur auf der Konsole ausgegeben.
|
* displayed on the console.
|
||||||
*/
|
*/
|
||||||
private void processBet() {
|
private void processBet() {
|
||||||
String input = taskbarInput.getText();
|
String input = taskbarInput.getText();
|
||||||
@@ -126,22 +125,22 @@ public class TaskbarController {
|
|||||||
int credits = Integer.parseInt(input.trim());
|
int credits = Integer.parseInt(input.trim());
|
||||||
|
|
||||||
if (credits >= MIN_CREDITS && credits <= MAX_CREDITS && credits % CREDIT_STEP == 0) {
|
if (credits >= MIN_CREDITS && credits <= MAX_CREDITS && credits % CREDIT_STEP == 0) {
|
||||||
// TODO: Credits müssen an die GameEngine gesendet werden
|
// TODO: Credits must be sent to the GameEngine
|
||||||
LOGGER.info("Einsatz gesetzt: {} Casono Credits", credits);
|
LOGGER.info("Bet set: {} Casono Credits", credits);
|
||||||
taskbarInput.clear();
|
taskbarInput.clear();
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Fehler: Nur 5er-Schritte (5, 10, ... 100.000) erlaubt!");
|
LOGGER.error("Error: Only increments of 5 (5, 10, ... 100,000) are allowed!");
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
LOGGER.info("Fehler: Bitte nur eine Zahl eingeben!");
|
LOGGER.error("Error: Please enter only one number!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Öffnet den integrierten Casono Webbrowser.
|
* Opens the integrated Casono web browser.
|
||||||
*
|
*
|
||||||
* <p>TODO: Ersetze die Start-URL durch die offizielle Projekt-Website (z.B. Tipps & Tricks
|
* <p>TODO: Replace the start URL with the official project website (e.g., Tips & Tricks page)
|
||||||
* Seite), sobald die Inhalte für Strategien und Support bereitstehen.
|
* once the content for strategies and support is available.
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private void onBrowserButtonClick() {
|
private void onBrowserButtonClick() {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingRequest;
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandlerExecutor;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
@@ -46,11 +47,12 @@ public class ServerApp {
|
|||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
CommandParserDispatcher dispatcher = new CommandParserDispatcher();
|
CommandParserDispatcher dispatcher = new CommandParserDispatcher();
|
||||||
CommandRouter router = new CommandRouter();
|
SessionManager sessionManager = new SessionManager(eventBus, dispatcher);
|
||||||
|
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
|
||||||
|
CommandHandlerExecutor handlerExecutor = new CommandHandlerExecutor(responseDispatcher);
|
||||||
|
CommandRouter router = new CommandRouter(handlerExecutor);
|
||||||
|
|
||||||
SessionManager sessionManager = new SessionManager(eventBus, dispatcher, router);
|
|
||||||
eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event));
|
eventBus.subscribe(DisconnectEvent.class, event -> sessionManager.onDisconnect(event));
|
||||||
NetworkManager networkManager = new NetworkManager(port, sessionManager);
|
|
||||||
|
|
||||||
UserRegistry userRegistry = new UserRegistry();
|
UserRegistry userRegistry = new UserRegistry();
|
||||||
eventBus.subscribe(
|
eventBus.subscribe(
|
||||||
@@ -71,10 +73,9 @@ public class ServerApp {
|
|||||||
SESSION_DISCONNECT_JOB_PERIOD,
|
SESSION_DISCONNECT_JOB_PERIOD,
|
||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
|
||||||
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
|
|
||||||
|
|
||||||
registerCommands(dispatcher, router, responseDispatcher, userRegistry);
|
registerCommands(dispatcher, router, responseDispatcher, userRegistry);
|
||||||
|
|
||||||
|
NetworkManager networkManager = new NetworkManager(port, sessionManager, router);
|
||||||
networkManager.start();
|
networkManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.checks;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class UserLoggedInCheck implements HandlerCheck {
|
||||||
|
private final UserRegistry userRegistry;
|
||||||
|
|
||||||
|
public UserLoggedInCheck(UserRegistry userRegistry) {
|
||||||
|
this.userRegistry = userRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Response> check(Request request) {
|
||||||
|
Optional<User> user = userRegistry.getBySessionId(request.getSessionId());
|
||||||
|
if (user.isPresent()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(
|
||||||
|
new ErrorResponse(
|
||||||
|
request.getContext(),
|
||||||
|
"USER_NOT_LOGGED_IN",
|
||||||
|
"The execution of this command requires the user to be logged in"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-3
@@ -7,8 +7,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatch
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/** Handles {@link CheckUsernameRequest}s to check whether a username is available. */
|
/** Handles {@link CheckUsernameRequest}s to check whether a username is available. */
|
||||||
public class CheckUsernameHandler implements CommandHandler<CheckUsernameRequest> {
|
public class CheckUsernameHandler extends CommandHandler<CheckUsernameRequest> {
|
||||||
private final ResponseDispatcher responseDispatcher;
|
|
||||||
private final UserRegistry userRegistry;
|
private final UserRegistry userRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +17,7 @@ public class CheckUsernameHandler implements CommandHandler<CheckUsernameRequest
|
|||||||
* @param userRegistry the registry used to look up existing users
|
* @param userRegistry the registry used to look up existing users
|
||||||
*/
|
*/
|
||||||
public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
||||||
this.responseDispatcher = responseDispatcher;
|
super(responseDispatcher);
|
||||||
this.userRegistry = userRegistry;
|
this.userRegistry = userRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -13,8 +13,7 @@ import java.util.Optional;
|
|||||||
* Handles {@link LoginRequest}s to create a user for a session, if the session has not assigned one
|
* Handles {@link LoginRequest}s to create a user for a session, if the session has not assigned one
|
||||||
* already
|
* already
|
||||||
*/
|
*/
|
||||||
public class LoginHandler implements CommandHandler<LoginRequest> {
|
public class LoginHandler extends CommandHandler<LoginRequest> {
|
||||||
private final ResponseDispatcher responseDispatcher;
|
|
||||||
private final UserRegistry userRegistry;
|
private final UserRegistry userRegistry;
|
||||||
private final UserFactory userFactory;
|
private final UserFactory userFactory;
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ public class LoginHandler implements CommandHandler<LoginRequest> {
|
|||||||
* @param userRegistry the registry used to look up existing users and create the new one
|
* @param userRegistry the registry used to look up existing users and create the new one
|
||||||
*/
|
*/
|
||||||
public LoginHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
public LoginHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
||||||
this.responseDispatcher = responseDispatcher;
|
super(responseDispatcher);
|
||||||
this.userRegistry = userRegistry;
|
this.userRegistry = userRegistry;
|
||||||
this.userFactory = new UserFactory(userRegistry);
|
this.userFactory = new UserFactory(userRegistry);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -7,8 +7,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkRespon
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
|
|
||||||
/** Handles {@link LogoutRequest} to logout connected user. */
|
/** Handles {@link LogoutRequest} to logout connected user. */
|
||||||
public class LogoutHandler implements CommandHandler<LogoutRequest> {
|
public class LogoutHandler extends CommandHandler<LogoutRequest> {
|
||||||
private final ResponseDispatcher responseDispatcher;
|
|
||||||
private final UserRegistry userRegistry;
|
private final UserRegistry userRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +17,7 @@ public class LogoutHandler implements CommandHandler<LogoutRequest> {
|
|||||||
* @param userRegistry registry responsible for tracking connected user sessions
|
* @param userRegistry registry responsible for tracking connected user sessions
|
||||||
*/
|
*/
|
||||||
public LogoutHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
public LogoutHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
||||||
this.responseDispatcher = responseDispatcher;
|
super(responseDispatcher);
|
||||||
this.userRegistry = userRegistry;
|
this.userRegistry = userRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -5,8 +5,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkRespon
|
|||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
|
|
||||||
/** Handler for {@link PingRequest}. */
|
/** Handler for {@link PingRequest}. */
|
||||||
public class PingHandler implements CommandHandler<PingRequest> {
|
public class PingHandler extends CommandHandler<PingRequest> {
|
||||||
private final ResponseDispatcher responseDispatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new PingHandler to execute {@link PingRequest}s
|
* Create a new PingHandler to execute {@link PingRequest}s
|
||||||
@@ -14,7 +13,7 @@ public class PingHandler implements CommandHandler<PingRequest> {
|
|||||||
* @param responseDispatcher dispatcher used to send responses back to clients
|
* @param responseDispatcher dispatcher used to send responses back to clients
|
||||||
*/
|
*/
|
||||||
public PingHandler(ResponseDispatcher responseDispatcher) {
|
public PingHandler(ResponseDispatcher responseDispatcher) {
|
||||||
this.responseDispatcher = responseDispatcher;
|
super(responseDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ public class Deck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cards of the deck. This method replaces the internal list
|
* Sets the cards of the deck. This method replaces the internal list with a copy of the
|
||||||
* with a copy of the provided list.
|
* provided list.
|
||||||
*
|
*
|
||||||
* @param cards the new list of cards to set
|
* @param cards the new list of cards to set
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandRouter;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -15,6 +16,7 @@ public class NetworkManager implements Runnable {
|
|||||||
private Thread thread;
|
private Thread thread;
|
||||||
private Boolean running;
|
private Boolean running;
|
||||||
private SessionManager sessionManager;
|
private SessionManager sessionManager;
|
||||||
|
private CommandRouter router;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new NetworkManager with the given port, session manager, and event bus.
|
* Creates a new NetworkManager with the given port, session manager, and event bus.
|
||||||
@@ -22,12 +24,13 @@ public class NetworkManager implements Runnable {
|
|||||||
* @param port the port to listen on
|
* @param port the port to listen on
|
||||||
* @param sessionManager the session manager to use
|
* @param sessionManager the session manager to use
|
||||||
*/
|
*/
|
||||||
public NetworkManager(Integer port, SessionManager sessionManager) {
|
public NetworkManager(Integer port, SessionManager sessionManager, CommandRouter router) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.logger = LogManager.getLogger(NetworkManager.class);
|
this.logger = LogManager.getLogger(NetworkManager.class);
|
||||||
this.thread = new Thread(this, "networkManager");
|
this.thread = new Thread(this, "networkManager");
|
||||||
this.running = true;
|
this.running = true;
|
||||||
this.sessionManager = sessionManager;
|
this.sessionManager = sessionManager;
|
||||||
|
this.router = router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Starts the internal thread to accept new connections. */
|
/** Starts the internal thread to accept new connections. */
|
||||||
@@ -45,7 +48,7 @@ public class NetworkManager implements Runnable {
|
|||||||
|
|
||||||
logger.debug("Accepted connection from {}", clientSocket.getRemoteSocketAddress());
|
logger.debug("Accepted connection from {}", clientSocket.getRemoteSocketAddress());
|
||||||
|
|
||||||
sessionManager.create(new TcpTransport(clientSocket));
|
sessionManager.create(new TcpTransport(clientSocket), router);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
+51
-2
@@ -1,7 +1,56 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface CommandHandler<T extends Request> {
|
/**
|
||||||
void execute(T request);
|
* Abstract base class for handling requests of a specific type.
|
||||||
|
*
|
||||||
|
* @param <T> the type of request this handler processes, must extend {@link Request}
|
||||||
|
*/
|
||||||
|
public abstract class CommandHandler<T extends Request> {
|
||||||
|
private final List<HandlerCheck> checks = new ArrayList<>();
|
||||||
|
protected final ResponseDispatcher responseDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a CommandHandler with the specified ResponseDispatcher.
|
||||||
|
*
|
||||||
|
* @param responseDispatcher the ResponseDispatcher instance used to send responses to clients.
|
||||||
|
*/
|
||||||
|
public CommandHandler(ResponseDispatcher responseDispatcher) {
|
||||||
|
this.responseDispatcher = responseDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a handler check to be performed before request execution.
|
||||||
|
*
|
||||||
|
* <p>The {@code execute} Method is only invoked, if all specified checks passed.
|
||||||
|
*
|
||||||
|
* @param check the {@link HandlerCheck} to add
|
||||||
|
*/
|
||||||
|
protected final void addCheck(HandlerCheck check) {
|
||||||
|
checks.add(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an unmodifiable list of all registered handler checks.
|
||||||
|
*
|
||||||
|
* @return an unmodifiable list of {@link HandlerCheck} objects
|
||||||
|
*/
|
||||||
|
public final List<HandlerCheck> getChecks() {
|
||||||
|
return Collections.unmodifiableList(checks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the request.
|
||||||
|
*
|
||||||
|
* <p>Subclasses should override this method to implement their specific request handling logic.
|
||||||
|
*
|
||||||
|
* @param request the request to execute, of type T
|
||||||
|
*/
|
||||||
|
public void execute(T request) {}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks.HandlerCheck;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes execution of the {@link CommandHandler} if all defined pre-execution checks pass.
|
||||||
|
*
|
||||||
|
* <p>If any check fails, the response returned by the check is dispatched immediately and execution
|
||||||
|
* is aborted. Otherwise, the handler will invoke execution normally.
|
||||||
|
*/
|
||||||
|
public class CommandHandlerExecutor {
|
||||||
|
private final ResponseDispatcher responseDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CommandHandlerExecutor to execute pre-execution checks and invoke the {@link
|
||||||
|
* CommandHandler} if all checks pass.
|
||||||
|
*
|
||||||
|
* @param responseDispatcher to dispatch the response with if an check fails
|
||||||
|
*/
|
||||||
|
public CommandHandlerExecutor(ResponseDispatcher responseDispatcher) {
|
||||||
|
this.responseDispatcher = responseDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a command handler if all defined pre-execution checks pass.
|
||||||
|
*
|
||||||
|
* @param handler the command handler containing checks and execution logic
|
||||||
|
* @param request the incoming request to validate and process
|
||||||
|
* @throws NullPointerException if handler or request is null
|
||||||
|
*/
|
||||||
|
public void execute(CommandHandler<Request> handler, Request request) {
|
||||||
|
for (HandlerCheck check : handler.getChecks()) {
|
||||||
|
Optional<Response> result = check.check(request);
|
||||||
|
if (result.isPresent()) {
|
||||||
|
responseDispatcher.dispatch(result.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.execute(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
-1
@@ -4,13 +4,46 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Routes incoming requests to their corresponding {@link CommandHandler} implementations. */
|
||||||
public class CommandRouter {
|
public class CommandRouter {
|
||||||
private final Map<Class<? extends Request>, CommandHandler<?>> handlers = new HashMap<>();
|
private final Map<Class<? extends Request>, CommandHandler<?>> handlers = new HashMap<>();
|
||||||
|
private final CommandHandlerExecutor handlerExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CommandRouter with the specified handler executor.
|
||||||
|
*
|
||||||
|
* @param commandHandlerExecutor to delegate execution of checks and invocation of {@link
|
||||||
|
* CommandHandler} to
|
||||||
|
*/
|
||||||
|
public CommandRouter(CommandHandlerExecutor commandHandlerExecutor) {
|
||||||
|
this.handlerExecutor = commandHandlerExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a {@link CommandHandler} for a specific request type.
|
||||||
|
*
|
||||||
|
* <p>This method establishes a type-safe mapping between a request class and its handler.
|
||||||
|
*
|
||||||
|
* <p>Only one handler can be registered per request type; subsequent registrations will
|
||||||
|
* overwrite previous ones.
|
||||||
|
*
|
||||||
|
* @param <T> the type of request handled by the provided handler
|
||||||
|
* @param request the request class to associate with the given command handler
|
||||||
|
* @param handler the command handler for the given request type
|
||||||
|
*/
|
||||||
public <T extends Request> void register(Class<T> request, CommandHandler<T> handler) {
|
public <T extends Request> void register(Class<T> request, CommandHandler<T> handler) {
|
||||||
handlers.put(request, handler);
|
handlers.put(request, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the appropriate handler for the given request.
|
||||||
|
*
|
||||||
|
* <p>Looks up the handler registered for the request's class and executes it through the
|
||||||
|
* configured {@link CommandHandlerExecutor}.
|
||||||
|
*
|
||||||
|
* @param request the request to be executed
|
||||||
|
* @throws UnknownRequestException if no handler is registered for the request type
|
||||||
|
*/
|
||||||
// Safe, because during registration, it's ensured that the provided CommandHandler only
|
// Safe, because during registration, it's ensured that the provided CommandHandler only
|
||||||
// receives requests it can handle.
|
// receives requests it can handle.
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -23,6 +56,7 @@ public class CommandRouter {
|
|||||||
throw new UnknownRequestException(
|
throw new UnknownRequestException(
|
||||||
"Unable to execute request " + requestName + ". Type unknown", requestName);
|
"Unable to execute request " + requestName + ". Type unknown", requestName);
|
||||||
}
|
}
|
||||||
handler.execute(request);
|
|
||||||
|
handlerExecutor.execute(handler, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.checks;
|
||||||
|
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.Response;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional interface used by pre-execution checks. Executed before the execute method of the
|
||||||
|
* {@link CommandHandler} is called
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface HandlerCheck {
|
||||||
|
/**
|
||||||
|
* Checks that the requirements outlined by this HandlerCheck are fulfilled.
|
||||||
|
*
|
||||||
|
* @param request to execute the check with
|
||||||
|
* @return Optional containing no value if the check was successful. And a {@link ErrorResponse}
|
||||||
|
* if the check failed, the response will be dispatched to the client.
|
||||||
|
*/
|
||||||
|
Optional<Response> check(Request request);
|
||||||
|
}
|
||||||
+3
-5
@@ -19,16 +19,13 @@ public class SessionManager {
|
|||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final CommandParserDispatcher dispatcher;
|
private final CommandParserDispatcher dispatcher;
|
||||||
private final CommandRouter router;
|
|
||||||
|
|
||||||
/** Constructs a new SessionManager. */
|
/** Constructs a new SessionManager. */
|
||||||
public SessionManager(
|
public SessionManager(EventBus eventBus, CommandParserDispatcher dispatcher) {
|
||||||
EventBus eventBus, CommandParserDispatcher dispatcher, CommandRouter router) {
|
|
||||||
this.sessions = new ConcurrentHashMap<>();
|
this.sessions = new ConcurrentHashMap<>();
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.logger = LogManager.getLogger(SessionManager.class);
|
this.logger = LogManager.getLogger(SessionManager.class);
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.router = router;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,9 +34,10 @@ public class SessionManager {
|
|||||||
* <p>Will create both worker threads and start them.
|
* <p>Will create both worker threads and start them.
|
||||||
*
|
*
|
||||||
* @param transport to create session from
|
* @param transport to create session from
|
||||||
|
* @param router the command router used by the created session
|
||||||
* @return newly created session
|
* @return newly created session
|
||||||
*/
|
*/
|
||||||
public Session create(TransportLayer transport) {
|
public Session create(TransportLayer transport, CommandRouter router) {
|
||||||
Session session = new Session(transport, eventBus, dispatcher, router);
|
Session session = new Session(transport, eventBus, dispatcher, router);
|
||||||
SessionReader reader = new SessionReader(session, eventBus);
|
SessionReader reader = new SessionReader(session, eventBus);
|
||||||
SessionWriter writer = new SessionWriter(session);
|
SessionWriter writer = new SessionWriter(session);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
.root {
|
.root {
|
||||||
-fx-background-color: #000000;
|
-fx-background-color: #000000;
|
||||||
-fx-font-family: "Monospaced", "Courier New"; /* Schriftart */
|
-fx-font-family: "Monospaced", "Courier New"; /* font */
|
||||||
}
|
}
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
-fx-background-image: url("/images/background.png");
|
-fx-background-image: url("/images/background.png");
|
||||||
-fx-background-size: cover; /* Bild füllt das ganze Fenster aus */
|
-fx-background-size: cover; /* Image fills the entire window */
|
||||||
/* Zentriert das Bild und verhindert, dass es sich kachelartig wiederholt */
|
/* Centers the image and prevents it from repeating in a tile-like pattern. */
|
||||||
-fx-background-position: center center;
|
-fx-background-position: center center;
|
||||||
-fx-background-repeat: no-repeat;
|
-fx-background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
@@ -181,20 +181,20 @@
|
|||||||
|
|
||||||
.status-label-small {
|
.status-label-small {
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
-fx-font-family: "Monospaced"; /* Schriftart */
|
-fx-font-family: "Monospaced"; /* font */
|
||||||
-fx-font-size: 15px;
|
-fx-font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-value-text {
|
.status-value-text {
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
-fx-font-family: "Monospaced"; /* Schriftart */
|
-fx-font-family: "Monospaced"; /* font */
|
||||||
-fx-font-size: 15px;
|
-fx-font-size: 15px;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-value-money {
|
.status-value-money {
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
-fx-font-family: "Monospaced"; /* Schriftart */
|
-fx-font-family: "Monospaced"; /* font */
|
||||||
-fx-font-size: 15px;
|
-fx-font-size: 15px;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.image.*?>
|
<?import javafx.scene.image.*?>
|
||||||
|
|
||||||
<!-- Hauptcontainer: Verknüpft die UI mit dem CasinoGameController und lädt Casinogameui.css -->
|
<!-- Main container: Links the UI to the CasinoGameController and loads Casinogameui.css -->
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx/21"
|
<AnchorPane xmlns="http://javafx.com/javafx/21"
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameController"
|
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameController"
|
||||||
@@ -14,28 +14,28 @@
|
|||||||
|
|
||||||
<GridPane prefWidth="1200" prefHeight="800" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0">
|
<GridPane prefWidth="1200" prefHeight="800" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<!-- Tisch-Box: 70% -->
|
<!-- Table-Box: 70% -->
|
||||||
<ColumnConstraints percentWidth="70.0" hgrow="ALWAYS" />
|
<ColumnConstraints percentWidth="70.0" hgrow="ALWAYS" />
|
||||||
<!-- Leere-Box: 5% -->
|
<!-- Table-Box: 5% -->
|
||||||
<ColumnConstraints percentWidth="5.0" hgrow="ALWAYS" />
|
<ColumnConstraints percentWidth="5.0" hgrow="ALWAYS" />
|
||||||
<!-- Chat-Box: 20% -->
|
<!-- Table-Box: 20% -->
|
||||||
<ColumnConstraints percentWidth="25.0" hgrow="ALWAYS" />
|
<ColumnConstraints percentWidth="25.0" hgrow="ALWAYS" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
|
|
||||||
<!-- Sorgt dafür, dass diese Zeile den gesamten verfügbaren vertikalen Platz nutzt -->
|
<!-- Ensures that this line uses all available vertical space -->
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints vgrow="ALWAYS" />
|
<RowConstraints vgrow="ALWAYS" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
|
|
||||||
<children>
|
<children>
|
||||||
<!-- Tisch-Box in Spalte 0 -->
|
<!-- Table box in column 0 -->
|
||||||
<StackPane GridPane.columnIndex="0">
|
<StackPane GridPane.columnIndex="0">
|
||||||
<!-- Innenabstand: Hält den Inhalt 10 Pixel von oben/unten und 20 Pixel von den Seiten fern -->
|
<!-- Inner spacing: Keeps the content 10 pixels away from the top/bottom and 20 pixels from the sides -->
|
||||||
<padding>
|
<padding>
|
||||||
<Insets top="20" right="10" bottom="120" left="20"/>
|
<Insets top="20" right="10" bottom="120" left="20"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|
||||||
<!-- Zentraler Casinotisch: Zentriert den Inhalt und nutzt durch die maximale Größe (1.7976931348623157E308) das gesamte Fenster -->
|
<!-- Central Casino Table: Centers the content and utilizes the entire window due to its maximum size (1.7976931348623157E308) -->
|
||||||
<VBox fx:id="casinoTable"
|
<VBox fx:id="casinoTable"
|
||||||
alignment="CENTER"
|
alignment="CENTER"
|
||||||
styleClass="casino-table"
|
styleClass="casino-table"
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
maxWidth="Infinity"
|
maxWidth="Infinity"
|
||||||
maxHeight="Infinity">
|
maxHeight="Infinity">
|
||||||
|
|
||||||
<!-- TODO: Platzhalter für die Poker-Spielfläche: Hier werden später dynamisch Karten, Chips und Einsätze der Gegner angezeigt -->
|
<!-- TODO: Placeholder for the poker playing area: Cards, chips and opponents' bets will be dynamically displayed here later -->
|
||||||
<HBox alignment="CENTER" spacing="15">
|
<HBox alignment="CENTER" spacing="15">
|
||||||
<VBox styleClass="dealer-box" prefWidth="80" prefHeight="120" />
|
<VBox styleClass="dealer-box" prefWidth="80" prefHeight="120" />
|
||||||
<VBox styleClass="dealer-box" prefWidth="80" prefHeight="120" />
|
<VBox styleClass="dealer-box" prefWidth="80" prefHeight="120" />
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<VBox alignment="CENTER" spacing="10">
|
<VBox alignment="CENTER" spacing="10">
|
||||||
<!-- Label mit Logo -->
|
<!-- Label with logo -->
|
||||||
<Label text="CAS0NO" styleClass="table-title">
|
<Label text="CAS0NO" styleClass="table-title">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="30.0" preserveRatio="true">
|
<ImageView fitHeight="30.0" preserveRatio="true">
|
||||||
@@ -63,32 +63,32 @@
|
|||||||
</graphic>
|
</graphic>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<!-- TODO: Platzhalter: Wird ersetzt, sobald die GameEngine fertig ist und echte Einsätze verarbeiten kann -->
|
<!-- TODO: Placeholder: Will be replaced once the game engine is finished and can process real-world scenarios -->
|
||||||
<Label fx:id="welcomeText" text="Setzen Sie Ihren Einsatz" styleClass="table-title" />
|
<Label fx:id="welcomeText" text="Setzen Sie Ihren Einsatz" styleClass="table-title" />
|
||||||
</VBox>
|
</VBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
|
|
||||||
<!-- Leere-Box in Spalte 1: Schafft Platz zwischen dem Tisch-Box und der Chat-Box -->
|
<!-- Empty box in column 1: Creates space between the table box and the chat box -->
|
||||||
<VBox GridPane.columnIndex="1"
|
<VBox GridPane.columnIndex="1"
|
||||||
alignment="CENTER"
|
alignment="CENTER"
|
||||||
minWidth="100"
|
minWidth="100"
|
||||||
minHeight="100">
|
minHeight="100">
|
||||||
<!-- Bleibt leer -->
|
<!-- Leave blank -->
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|
||||||
<!-- TODO: Platzhalter: Chat-Box in Spalte 2: -->
|
<!-- TODO: Placeholder: Chat box in column 2: -->
|
||||||
<fx:include source="components/Chatbox.fxml" GridPane.columnIndex="2"/>
|
<fx:include source="components/Chatbox.fxml" GridPane.columnIndex="2"/>
|
||||||
|
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|
||||||
<!-- verschiebbare Taskbar -->
|
<!-- movable taskbar -->
|
||||||
<AnchorPane>
|
<AnchorPane>
|
||||||
<fx:include fx:id="taskbarInclude" source="gameuicomponents/Taskbar.fxml"/>
|
<fx:include fx:id="taskbarInclude" source="gameuicomponents/Taskbar.fxml"/>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|
||||||
<!-- Gegner-Status 1: Eines von drei Panels, das Icon, Name und Kontostand des Mitspielers anzeigt -->
|
<!-- Opponent Status 1: One of three panels displaying the opponent's icon, name, and account balance -->
|
||||||
<GridPane AnchorPane.leftAnchor="0.0"
|
<GridPane AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0"
|
AnchorPane.rightAnchor="0.0"
|
||||||
AnchorPane.topAnchor="50">
|
AnchorPane.topAnchor="50">
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|
||||||
<!-- Gegner-Status 2: Eines von drei Panels, das Icon, Name und Kontostand des Mitspielers anzeigt -->
|
<!-- Opponent Status 2: One of three panels displaying the opponent's icon, name, and account balance -->
|
||||||
<GridPane AnchorPane.leftAnchor="0.0"
|
<GridPane AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0"
|
AnchorPane.rightAnchor="0.0"
|
||||||
AnchorPane.topAnchor="20">
|
AnchorPane.topAnchor="20">
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|
||||||
<!-- Gegner-Status 3: Eines von drei Panels, das Icon, Name und Kontostand des Mitspielers anzeigt -->
|
<!-- Opponent Status 3: One of three panels that displays the opponent's icon, name, and account balance -->
|
||||||
<GridPane AnchorPane.leftAnchor="0.0"
|
<GridPane AnchorPane.leftAnchor="0.0"
|
||||||
AnchorPane.rightAnchor="0.0"
|
AnchorPane.rightAnchor="0.0"
|
||||||
AnchorPane.topAnchor="50">
|
AnchorPane.topAnchor="50">
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Dieses Fenster wird später mit den Serveranfragen verknüpft,
|
This window will later be linked to the server requests,
|
||||||
um Nachrichten von Mitspielern und Systemmeldungen in Echtzeit anzuzeigen.
|
to display messages from other players and system messages in real time.
|
||||||
|
|
||||||
Der Chat unterscheidet intern zwischen:
|
The chat internally distinguishes between:
|
||||||
- Player-to-Player (Privater 2-Personen-Chat)
|
- Player-to-Player (Private 2-person chat)
|
||||||
- Lobby-Chat (Aktueller Raum)
|
- Lobby Chat (Current room)
|
||||||
- Globaler Chat (Gesamter Server)
|
- Global Chat (Entire server)
|
||||||
|
|
||||||
Features, die später implementiert werden sollen:
|
Features planned for later implementation:
|
||||||
- Schicke Chat-Bubbles mit Namen und Uhrzeit.
|
- Stylish chat bubbles with names and timestamps.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<VBox xmlns="http://javafx.com/javafx/21"
|
<VBox xmlns="http://javafx.com/javafx/21"
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
alignment="CENTER"
|
alignment="CENTER"
|
||||||
stylesheets="@Chatui.css">
|
stylesheets="@Chatui.css">
|
||||||
|
|
||||||
<!-- Innenabstand: Schafft oben, rechts und unten 30 Pixel Platz, links nur 10 Pixel (asymmetrisch) -->
|
<!-- Inner spacing: Creates 30 pixels of space at the top, right and bottom, only 10 pixels on the left (asymmetrical) -->
|
||||||
<padding>
|
<padding>
|
||||||
<Insets top="30" right="30" bottom="30" left="10"/>
|
<Insets top="30" right="30" bottom="30" left="10"/>
|
||||||
</padding>
|
</padding>
|
||||||
@@ -38,16 +38,16 @@
|
|||||||
<Label text="== CHAT ==" styleClass="chat-header" alignment="CENTER" maxWidth="Infinity"/>
|
<Label text="== CHAT ==" styleClass="chat-header" alignment="CENTER" maxWidth="Infinity"/>
|
||||||
<Region minHeight="4" styleClass="chat-separator"/>
|
<Region minHeight="4" styleClass="chat-separator"/>
|
||||||
|
|
||||||
<!-- Chatnachrichten werden hier hinzugefügt -->
|
<!-- Chat messages will be added here -->
|
||||||
<ScrollPane fx:id="chatScrollPane" fitToWidth="true" vbarPolicy="AS_NEEDED" hbarPolicy="NEVER" VBox.vgrow="ALWAYS" styleClass="chat-scroll-pane">
|
<ScrollPane fx:id="chatScrollPane" fitToWidth="true" vbarPolicy="AS_NEEDED" hbarPolicy="NEVER" VBox.vgrow="ALWAYS" styleClass="chat-scroll-pane">
|
||||||
<content>
|
<content>
|
||||||
<VBox fx:id="chatVBox" spacing="10" styleClass="chat-VBox"/>
|
<VBox fx:id="chatVBox" spacing="10" styleClass="chat-VBox"/>
|
||||||
</content>
|
</content>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
|
|
||||||
<!-- Eingabebereich -->
|
<!-- Input area -->
|
||||||
<HBox spacing="10">
|
<HBox spacing="10">
|
||||||
<!-- TODO: Größe des TextFields dynamisch anpassen -->
|
<!-- TODO: Dynamically adjust the size of the text field -->
|
||||||
<TextField fx:id="inputField" HBox.hgrow="ALWAYS" promptText="Nachricht eingeben..." styleClass="gray-input-field" onAction="#sendMessage"/>
|
<TextField fx:id="inputField" HBox.hgrow="ALWAYS" promptText="Nachricht eingeben..." styleClass="gray-input-field" onAction="#sendMessage"/>
|
||||||
<Button fx:id="sendButton" text="SENDEN" onAction="#sendMessage" styleClass="yellow-button"/>
|
<Button fx:id="sendButton" text="SENDEN" onAction="#sendMessage" styleClass="yellow-button"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
TODO: Haupt-platzhalter für spieler-status & eingabe.
|
TODO: Main placeholder for player status & input.
|
||||||
Dieses Layout dient aktuell der visuellen Struktur. Sobald die GameEngine und
|
|
||||||
Serveranfragen (API/Requests) durchgeführt werden können, werden Name, Kontostand
|
This layout currently serves as a visual structure. As soon as the game engine and
|
||||||
und Status-Anzeigen dynamisch mit Echtzeit-Daten vom Server befüllt.
|
server requests (API/requests) can be processed, the name, account balance
|
||||||
|
and status displays will be dynamically populated with real-time data from the server.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<VBox fx:id="playerStatusBox"
|
<VBox fx:id="playerStatusBox"
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
spacing="0">
|
spacing="0">
|
||||||
|
|
||||||
<children>
|
<children>
|
||||||
<!-- Bereich für den Spielernamen -->
|
<!-- Area for player name -->
|
||||||
<HBox styleClass="status-inner-box-top"
|
<HBox styleClass="status-inner-box-top"
|
||||||
alignment="CENTER_LEFT"
|
alignment="CENTER_LEFT"
|
||||||
maxWidth="Infinity"
|
maxWidth="Infinity"
|
||||||
@@ -31,11 +32,11 @@
|
|||||||
</padding>
|
</padding>
|
||||||
|
|
||||||
<Label text="NAME: " styleClass="status-label-small"/>
|
<Label text="NAME: " styleClass="status-label-small"/>
|
||||||
<!-- TODO: fx:id="playerName": Wird später durch den User-Namen aus der Server-Abfrage ersetzt -->
|
<!-- TODO: fx:id="playerName": Will later be replaced by the username from the server query -->
|
||||||
<Label fx:id="playerName" text="SPIELER" styleClass="status-value-text"/>
|
<Label fx:id="playerName" text="SPIELER" styleClass="status-value-text"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<!-- Schaft platz zwischen den Boxen -->
|
<!-- Shaft space between the boxes -->
|
||||||
<HBox styleClass="status-inner-box-midle"
|
<HBox styleClass="status-inner-box-midle"
|
||||||
alignment="CENTER_LEFT"
|
alignment="CENTER_LEFT"
|
||||||
maxWidth="Infinity"
|
maxWidth="Infinity"
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
</padding>
|
</padding>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<!-- Anzeige des Kapitals/Geldstands -->
|
<!-- Display of capital/money balance -->
|
||||||
<HBox styleClass="status-inner-box-bottom"
|
<HBox styleClass="status-inner-box-bottom"
|
||||||
alignment="CENTER_LEFT"
|
alignment="CENTER_LEFT"
|
||||||
maxWidth="Infinity"
|
maxWidth="Infinity"
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
<Label fx:id="playerMoney" text="0 $" styleClass="status-value-money"/>
|
<Label fx:id="playerMoney" text="0 $" styleClass="status-value-money"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<!-- TODO: User-Logo oder Icon: Platzhalter für das Profilbild oder das individuelle Logo des Spielers -->
|
<!-- TODO: User logo or icon: Placeholder for the profile picture or the player's individual logo -->
|
||||||
<Pane prefHeight="0" maxWidth="Infinity">
|
<Pane prefHeight="0" maxWidth="Infinity">
|
||||||
<Region styleClass="status-circle"
|
<Region styleClass="status-circle"
|
||||||
layoutX="-10"
|
layoutX="-10"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
|
||||||
<!-- verschiebbare Taskbar -->
|
<!-- movable taskbar -->
|
||||||
<HBox xmlns="http://javafx.com/javafx/21"
|
<HBox xmlns="http://javafx.com/javafx/21"
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.TaskbarController"
|
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.TaskbarController"
|
||||||
@@ -17,36 +17,36 @@
|
|||||||
onMouseReleased="#onTaskbarReleased"
|
onMouseReleased="#onTaskbarReleased"
|
||||||
layoutX="50.0" layoutY="710.0">
|
layoutX="50.0" layoutY="710.0">
|
||||||
|
|
||||||
<!-- Platzhalter-Buttons -->
|
<!-- Placeholder buttons -->
|
||||||
<Button text="EINSTELLUNGEN" styleClass="gray-button" />
|
<Button text="EINSTELLUNGEN" styleClass="gray-button" />
|
||||||
<Button text="INFO" styleClass="gray-button" />
|
<Button text="INFO" styleClass="gray-button" />
|
||||||
<!-- Buten für den Casono Browser -->
|
<!-- Buten for the Casono Browser -->
|
||||||
<Button text="HILFE" onAction="#onBrowserButtonClick" styleClass="gray-button" />
|
<Button text="HILFE" onAction="#onBrowserButtonClick" styleClass="gray-button" />
|
||||||
|
|
||||||
<!-- Vertikale-Trennlinie: Erzeugt eine optische Abgrenzung zwischen zwei Butten-Bereichen -->
|
<!-- Vertical dividing line: Creates a visual separation between two button areas -->
|
||||||
<Separator orientation="VERTICAL" prefHeight="20" opacity="0.3" />
|
<Separator orientation="VERTICAL" prefHeight="20" opacity="0.3" />
|
||||||
|
|
||||||
<!-- Eingabefeld: Reagiert auf die Enter-Taste, um den Einsatz zu bestätigen -->
|
<!-- Input field: Responds to the Enter key to confirm the entry -->
|
||||||
<TextField fx:id="taskbarInput"
|
<TextField fx:id="taskbarInput"
|
||||||
promptText="Betrag setzen..."
|
promptText="Betrag setzen..."
|
||||||
onKeyPressed="#onInputSubmitted"
|
onKeyPressed="#onInputSubmitted"
|
||||||
styleClass="gray-input-field"
|
styleClass="gray-input-field"
|
||||||
prefWidth="150" />
|
prefWidth="150" />
|
||||||
|
|
||||||
<!-- Bestätigungs-Button: Alternative zum Enter-Key, um den Einsatz abzusenden -->
|
<!-- Confirmation button: Alternative to the Enter key to submit the entry -->
|
||||||
<Button text="SETZTEN"
|
<Button text="SETZTEN"
|
||||||
onAction="#onInputSubmittedAction"
|
onAction="#onInputSubmittedAction"
|
||||||
styleClass="yellow-button"/>
|
styleClass="yellow-button"/>
|
||||||
|
|
||||||
<!-- Vertikale-Trennlinie: Erzeugt eine optische Abgrenzung zwischen zwei Butten-Bereichen -->
|
<!-- Vertical dividing line: Creates a visual separation between two button areas -->
|
||||||
<Separator orientation="VERTICAL" prefHeight="20" opacity="0.3" />
|
<Separator orientation="VERTICAL" prefHeight="20" opacity="0.3" />
|
||||||
|
|
||||||
<!-- Menü-Button: Bricht das aktuelle Spiel ab und kehrt zum Hauptmenü zurück -->
|
<!-- Menu button: Cancels the current game and returns to the main menu -->
|
||||||
<Button text="VERLASSEN"
|
<Button text="VERLASSEN"
|
||||||
onAction="#onExitButtonClick"
|
onAction="#onExitButtonClick"
|
||||||
styleClass="red-button"/>
|
styleClass="red-button"/>
|
||||||
|
|
||||||
<!-- Innenabstand: Hält den Inhalt 10 Pixel von oben/unten und 20 Pixel von den Seiten fern -->
|
<!-- Inner spacing: Keeps the content 10 pixels away from the top/bottom and 20 pixels from the sides -->
|
||||||
<padding>
|
<padding>
|
||||||
<Insets top="10" right="20" bottom="10" left="20"/>
|
<Insets top="10" right="20" bottom="10" left="20"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
Reference in New Issue
Block a user