diff --git a/documents/docs/networking/commands/README.md b/documents/docs/networking/commands/README.md new file mode 100644 index 0000000..2784975 --- /dev/null +++ b/documents/docs/networking/commands/README.md @@ -0,0 +1,31 @@ +# Commands +Commands are the primary extension point of the server. +Every client-facing operation e.g. checking a username, sending a chat message, joining a lobby is implemented as a command. +Each command consists of four classes: a **Parser**, a **Request**, a **Handler**, and a **Response**. + +The infrastructure for routing and dispatching commands lives in the `network/` layer and is intentionally kept generic. +The concrete implementations for each command live in `app/commands/` and are wired together at startup in `ServerApp`. + +![Overview of all components directly related to executing requests](/documents/images/docs/networking/commands/overview.svg) + +## Contents +### Guides +- [Implementing a Command](./guide-on-implementing-a-command.md) - + Step-by-step walkthrough for adding a new command to the server, including registration and common pitfalls. + +### Reference +- [Command Infrastructure](./commands-deep-dive.md) - + Technical deep-dive into the `CommandParser`, `CommandParserDispatcher`, `CommandHandler`, `CommandRouter`, `Request`, and the response hierarchy. + +## Key Concepts +**Each command is self-contained.** +A command's Parser, Request, Handler, and Response all live in the same package under `app/commands//`. +This keeps related code co-located and makes it easy to reason about a single command without navigating across multiple directories. + +**The `network/` layer knows nothing about specific commands.** +`CommandParser` and `CommandHandler` are generic interfaces. The `CommandParserDispatcher` and `CommandRouter` operate on those interfaces. +Adding a new command **never** requires modifying infrastructure code. + +**Registration happens at the composition root.** +All commands are wired in `ServerApp` by calling `parserDispatcher.register(...)` and `commandRouter.register(...)`. +This keeps the wiring explicit and compiler-checked. diff --git a/documents/docs/networking/commands/commands-deep-dive.md b/documents/docs/networking/commands/commands-deep-dive.md new file mode 100644 index 0000000..3999384 --- /dev/null +++ b/documents/docs/networking/commands/commands-deep-dive.md @@ -0,0 +1,108 @@ +# Command Infrastructure +This document describes the generic command infrastructure that lives in the `network/` layer. +It covers the parsing pipeline, the routing pipeline, and the base types that every command builds on. + +The infrastructure is intentionally project-agnostic: It has no knowledge of specific commands and is never modified when a new command is added. + +## Overview +An incoming request travels through two sequential pipelines: **parsing** and **execution**. + +The parsing pipeline converts a stringly-typed `PrimitiveRequest` into a strongly-typed `Request` subclass. +The execution pipeline routes that typed request to the correct handler, which produces a `Response`. + +![Sequence diagram of all involved components to process and respond to an incoming request](/documents/images/docs/networking/commands/sequence_diagram.svg) + +## Parsing Pipeline +### `CommandParser` +![Class diagram of the CommandParser](/documents/images/docs/networking/commands/command_parser.svg) + +The `CommandParser` is a single-method interface responsible for converting a `PrimitiveRequest` into a concrete, typed `Request` subclass. +Implementations live in `app/commands//` and are registered by name in `CommandParserDispatcher`. + +The parser is the correct place to validate and extract parameters. +If a required parameter is absent, `RequestParameterAccessor.require(...)` throws an `MissingParameterException`, which the `SessionReader` catches and converts into a `MISSING_PARAMETER` error response for the client. + +Parsers **do not** perform any domain logic. Their only job is extraction and type conversion. + +### `CommandParserDispatcher` +![Class diagram of CommandParserDispatcher](/documents/images/docs/networking/commands/command_parser_dispatcher.svg) + +The `CommandParserDispatcher` holds a map from command name strings (e.g. `"PING"`) to their corresponding `CommandParser`. +When the `SessionReader` receives a `PrimitiveRequest`, it calls `dispatcher.parse(...)`, which looks up the parser by the request's command string and delegates parsing. + +If no parser is registered for the command name, `parse(...)` throws an `UnknownCommandException`, which `SessionReader` catches and converts into an `UNKNOWN_COMMAND` error response for the client. + +### `RequestParameterAccessor` +![Class diagram of RequestParameterAccessor](/documents/images/docs/networking/commands/request_parameter_accessor.svg) + +The `RequestParameterAccessor` is a helper provided to parsers for reading typed parameter values from a `PrimitiveRequest`. +It indexes the parameter list by key on construction for O(1) lookups. + +```java +// Require a parameter — throws MissingParameterException if absent +String username = accessor.require("USERNAME"); + +// Require and parse — throws ParameterParseException if conversion fails +int count = accessor.require("COUNT", Integer::parseInt); + +// Optional with a default +String mode = accessor.optional("MODE", "default"); +``` + +The `ThrowingParser` functional interface accepted by the typed overloads allows any checked or unchecked exception to propagate from the conversion function. +The `RequestParameterAccessor` wraps it in a `ParameterParseException`. + +## Execution Pipeline +### `Request` +![Class diagram of Request](/documents/images/docs/networking/commands/request.svg) + +The `Request` is the abstract base class for all typed command requests. It carries a `RequestContext`, an immutable record containing the originating `SessionId` and the numeric `requestId`. +Both of which are later used by the handler to direct the response to the correct session. + +Concrete subclasses add command-specific fields, all set via constructor. Requests are immutable value objects. They carry data, not behaviour. + +### `CommandHandler` +![Class diagram of CommandHandler](/documents/images/docs/networking/commands/command_handler.svg) + +The `CommandHandler` is a single-method interface 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`. + +Handlers receive their dependencies (the `ResponseDispatcher`, registries and managers etc.) through constructor injection. +This keeps them fully testable without the network layer. + +### `CommandRouter` +![Class diagram of CommandRouter](/documents/images/docs/networking/commands/command_router.svg) + +The `CommandRouter` maps `Request` subclasses to their handlers using the request's runtime class as the key. +The type safety of `register(...)` ensures that a handler can only be registered for the exact type it is parameterised on. +The unchecked cast in `execute(...)` is therefore safe by construction and is documented with a `@SuppressWarnings` comment in the source. + +If no handler is registered for the given request type, `execute(...)` throws `UnknownRequestException`. +Unlike `UnknownCommandException` (which covers unknown command strings), this exception indicates a programming error i.e. a parser was registered without a corresponding handler. + +## Response Types +![Class diagram of the response interface and built-in implementations](/documents/images/docs/networking/commands/response_types.svg) + +The `Response` is the abstract base for all server responses. Its two concrete branches are `SuccessResponse` (prefix `+OK`) and `ErrorResponse` (prefix `-ERR`). +Command-specific responses extend `SuccessResponse` and populate the body using the `ResponseBodyBuilder`. + +`OkResponse` is a pre-built convenience subclass of `SuccessResponse` with an empty body, used for commands that need only acknowledge success without returning data (e.g. `PING`). + +The body is built with a fluent `ResponseBodyBuilder`: + +```java +// Simple key/value parameters +new ResponseBodyBuilder() + .param("STATUS", UsernameAvailability.FREE) + .build(); + +// Nested block +new ResponseBodyBuilder() + .block("USER", b -> b + .param("ID", user.getId().value()) + .param("NAME", user.getName())) + .build(); +``` + +`ResponseEncoder` serialises the body into the wire format (tab-indented, `END`-terminated blocks) and wraps it in a `PrimitiveResponse`. +`ResponseDispatcher` then enqueues this into the target session's bounded response queue. diff --git a/documents/docs/networking/commands/guide-on-implementing-a-command.md b/documents/docs/networking/commands/guide-on-implementing-a-command.md new file mode 100644 index 0000000..037251f --- /dev/null +++ b/documents/docs/networking/commands/guide-on-implementing-a-command.md @@ -0,0 +1,215 @@ +# Implementing a Command +This guide walks through the full process of adding a new command to the server. By the end you +will have a working command with a Parser, Request, Handler, and Response, all correctly wired +into the server. + +For background on how these components interact at a technical level, see [Command Infrastructure](../reference/command-infrastructure.md). + +## Before You Start +A command consists of exactly four classes, all placed in the same package: + +``` +app/commands// + YourCommandParser.java + YourCommandRequest.java + YourCommandHandler.java + YourCommandResponse.java ← omit this if OkResponse is sufficient +``` + +Name the package after the command in `snake_case`, matching the wire-protocol name (e.g. `join_lobby` for the `JOIN_LOBBY` command). +Name the classes in `UpperCamelCase` with the command name as prefix. + +## Step 1 — Define the Request + +The `Request` subclass is a typed, immutable value object holding everything the handler needs. +Define it first, because both the Parser and the Handler depend on it. + +```java +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.greet; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; + +public class GreetRequest extends Request { + private final String name; + + public GreetRequest(RequestContext context, String name) { + super(context); + this.name = name; + } + + public String getName() { + return name; + } +} +``` + +**Rules for the Request class:** + +- Always pass `context` directly to `super(context)`. Never store it in a separate field. +- Fields must be `private final`. Set them only through the constructor. +- Provide a getter for every field. No setters. +- No logic. The request is data, not behaviour. + + + +## Step 2 — Implement the Parser + +The Parser extracts parameters from the `PrimitiveRequest` and constructs the typed Request. + +```java +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.greet; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor; + +public class GreetParser implements CommandParser { + @Override + public GreetRequest parse(PrimitiveRequest primitiveRequest) { + RequestParameterAccessor accessor = new RequestParameterAccessor(primitiveRequest.parameters()); + String name = accessor.require("NAME"); + return new GreetRequest(primitiveRequest.context(), name); + } +} +``` + +**Rules for the Parser class:** + +- Always create a `RequestParameterAccessor` from `primitiveRequest.parameters()`. +- Use `accessor.require(key)` for mandatory parameters. It throws `MissingParameterException` + automatically — do not write your own null checks. +- Use `accessor.optional(key, defaultValue)` for optional parameters. +- Use the typed overloads (e.g. `accessor.require("COUNT", Integer::parseInt)`) for non-string + parameters. The resulting `ParameterParseException` is handled by `SessionReader`. +- Always pass `primitiveRequest.context()` as the first argument to the Request constructor. +- No domain logic. + +### Choosing between `require` and `optional` + +| Use | When | +|-|-| +| `accessor.require(key)` | The command cannot function without this parameter | +| `accessor.require(key, parser)` | Same, but the value must be converted to a specific type | +| `accessor.optional(key, default)` | The parameter has a sensible default when omitted | +| `accessor.optional(key, default, parser)` | Optional + type conversion | + + + +## Step 3 — Implement the Response + +If your command returns data, create a dedicated Response class. If it only needs to signal +success, use `OkResponse` directly in the handler and skip this step. + +```java +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.greet; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder; + +public class GreetResponse extends SuccessResponse { + public GreetResponse(RequestContext context, String greeting) { + super(context, new ResponseBodyBuilder() + .param("GREETING", greeting) + .build()); + } +} +``` + +**Rules for the Response class:** + +- Extend `SuccessResponse` for successful outcomes, not `Response` directly. +- Build the body inline in the `super(...)` call using `ResponseBodyBuilder`. Do not store + the builder or body separately. +- Use `ResponseBodyBuilder.block(tag, consumer)` to add nested structures when the response + carries a list or a complex sub-object. +- Parameter keys must be `UPPER_SNAKE_CASE` to match the wire protocol convention. +- If you need to return an error (e.g. lobby not found), do not throw — dispatch an + `ErrorResponse` from the handler instead (see Step 4). + + + +## Step 4 — Implement the Handler + +The Handler contains the domain logic. It reads from registries, modifies state if needed, and +always dispatches exactly one response. + +```java +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.greet; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher; + +public class GreetHandler implements CommandHandler { + public final ResponseDispatcher responseDispatcher; + + public GreetHandler(ResponseDispatcher responseDispatcher) { + this.responseDispatcher = responseDispatcher; + } + + @Override + public void execute(GreetRequest request) { + GreetResponse response = new GreetResponse(request.getContext(), "Hello " + request.getName() + ", nice to meet you"); + responseDispatcher.dispatch(response); + } +} +``` + +**Rules for the Handler class:** + +- Declare all dependencies as `private final` fields, injected through the constructor. +- Always dispatch exactly one response per execution path. Every branch must end with a + `responseDispatcher.dispatch(...)` call. +- Use `ErrorResponse` for domain-level failures (e.g. entity not found, precondition not met). + Do not throw exceptions for expected failure cases. +- Use `request.getContext()` when constructing any Response — never construct a `RequestContext` + yourself. +- Do not call `responseDispatcher.dispatch(...)` more than once in a single `execute` invocation. + + + +## Step 5 — Register the Command + +Open `ServerApp.registerCommands(...)` and add two lines: one to register the parser and one to +register the handler. + +```java +private static void registerCommands( + CommandParserDispatcher parserDispatcher, + CommandRouter commandRouter, + ResponseDispatcher responseDispatcher /* add new dependencies here */) { + // ... existing commands ... + + parserDispatcher.register("GREET", new GreetParser()); + commandRouter.register(GreetRequest.class, + new GreetHandler(responseDispatcher)); +} +``` + +The string passed to `parserDispatcher.register(...)` must exactly match the command name as +sent by the client on the wire, in `UPPER_SNAKE_CASE`. + +> **Important:** Always register both the parser **and** the handler. Registering a parser +> without a handler will result in an `UnknownRequestException` at runtime when the command is +> received — the parser will succeed, but the router will find no handler for the resulting +> request type. + + + +## Common Mistakes + +**Forgetting to pass `context` through to the Response.** The `context` is how the response +finds its way back to the right client. Dropping it means the response is dispatched to the +wrong session or causes a NullPointerException. + +**Putting domain logic in the Parser.** Parsers run before the request is validated as +meaningful. A parser that calls a registry or modifies state creates hidden coupling between the +parsing and execution phases and makes the parser difficult to test. + +**Dispatching a response before an early return.** A common mistake is to dispatch an error +and then fall through to dispatch a success response as well. Always `return` immediately after +dispatching an error. + +**Using a raw string for the error code.** Error codes should be `UPPER_SNAKE_CASE` constant +strings that the client can match against programmatically. Avoid spaces or punctuation. diff --git a/documents/images/docs/networking/commands/command_handler.puml b/documents/images/docs/networking/commands/command_handler.puml new file mode 100644 index 0000000..35337aa --- /dev/null +++ b/documents/images/docs/networking/commands/command_handler.puml @@ -0,0 +1,8 @@ +@startuml +skinparam backgroundColor transparent + +interface CommandHandler { + + execute(request: T): void +} + +@enduml diff --git a/documents/images/docs/networking/commands/command_handler.svg b/documents/images/docs/networking/commands/command_handler.svg new file mode 100644 index 0000000..750344e --- /dev/null +++ b/documents/images/docs/networking/commands/command_handler.svg @@ -0,0 +1 @@ +CommandHandlerT extends Requestexecute(request: T): void \ No newline at end of file diff --git a/documents/images/docs/networking/commands/command_parser.puml b/documents/images/docs/networking/commands/command_parser.puml new file mode 100644 index 0000000..80df3aa --- /dev/null +++ b/documents/images/docs/networking/commands/command_parser.puml @@ -0,0 +1,8 @@ +@startuml +skinparam backgroundColor transparent + +interface CommandParser { + + parse(primitiveRequest: PrimitiveRequest): T +} + +@enduml diff --git a/documents/images/docs/networking/commands/command_parser.svg b/documents/images/docs/networking/commands/command_parser.svg new file mode 100644 index 0000000..357abf4 --- /dev/null +++ b/documents/images/docs/networking/commands/command_parser.svg @@ -0,0 +1 @@ +CommandParserT extends Requestparse(primitiveRequest: PrimitiveRequest): T \ No newline at end of file diff --git a/documents/images/docs/networking/commands/command_parser_dispatcher.puml b/documents/images/docs/networking/commands/command_parser_dispatcher.puml new file mode 100644 index 0000000..6079366 --- /dev/null +++ b/documents/images/docs/networking/commands/command_parser_dispatcher.puml @@ -0,0 +1,11 @@ +@startuml +skinparam backgroundColor transparent + +class CommandParserDispatcher { + - parsers: Map + + + register(command: String, parser: CommandParser): void + + parse(primitiveRequest: PrimitiveRequest): Request +} + +@enduml diff --git a/documents/images/docs/networking/commands/command_parser_dispatcher.svg b/documents/images/docs/networking/commands/command_parser_dispatcher.svg new file mode 100644 index 0000000..7f61e6e --- /dev/null +++ b/documents/images/docs/networking/commands/command_parser_dispatcher.svg @@ -0,0 +1 @@ +CommandParserDispatcherparsers: Map<String, CommandParser>register(command: String, parser: CommandParser): voidparse(primitiveRequest: PrimitiveRequest): Request \ No newline at end of file diff --git a/documents/images/docs/networking/commands/command_router.puml b/documents/images/docs/networking/commands/command_router.puml new file mode 100644 index 0000000..210444f --- /dev/null +++ b/documents/images/docs/networking/commands/command_router.puml @@ -0,0 +1,10 @@ +@startuml +skinparam backgroundColor transparent + +class CommandRouter { + - handlers: Map, CommandHandler> + + register(requestClass: Class, handler: CommandHandler): void + + execute(request: Request): void +} + +@enduml diff --git a/documents/images/docs/networking/commands/command_router.svg b/documents/images/docs/networking/commands/command_router.svg new file mode 100644 index 0000000..7dc0117 --- /dev/null +++ b/documents/images/docs/networking/commands/command_router.svg @@ -0,0 +1 @@ +CommandRouterhandlers: Map<Class<? extends Request>, CommandHandler<?>>register(requestClass: Class<T>, handler: CommandHandler<T>): voidexecute(request: Request): void \ No newline at end of file diff --git a/documents/images/docs/networking/commands/overview.puml b/documents/images/docs/networking/commands/overview.puml new file mode 100644 index 0000000..da85ed7 --- /dev/null +++ b/documents/images/docs/networking/commands/overview.puml @@ -0,0 +1,33 @@ +@startuml +skinparam backgroundColor transparent + +package "network/ (infrastructure)" { + class PrimitiveRequest <> + interface CommandParser + interface CommandHandler + class CommandParserDispatcher + class CommandRouter + abstract class Request + abstract class Response + + PrimitiveRequest --> CommandParserDispatcher : routes + PrimitiveRequest ..> CommandParser : parsed by + CommandParserDispatcher --> CommandParser : routes to + CommandParser --> Request : parses to + Request --> CommandRouter : routes + CommandRouter --> CommandHandler : routes to + Request ..> CommandHandler : executed by + CommandHandler --> Response : creates +} + +package "app/commands// (per command)" { + class ExampleParser implements CommandParser + class ExampleRequest extends Request + class ExampleHandler implements CommandHandler + class ExampleResponse extends Response + + ExampleParser --> ExampleRequest : parses to + ExampleRequest --> ExampleHandler : executed by + ExampleHandler --> ExampleResponse : produces +} +@enduml diff --git a/documents/images/docs/networking/commands/overview.svg b/documents/images/docs/networking/commands/overview.svg new file mode 100644 index 0000000..8bc887d --- /dev/null +++ b/documents/images/docs/networking/commands/overview.svg @@ -0,0 +1 @@ +network/ (infrastructure)app/commands/<name>/ (per command)«record»PrimitiveRequestCommandParserT extends RequestCommandHandlerT extends RequestCommandParserDispatcherCommandRouterRequestResponseExampleParserCommandParserExampleRequestRequestExampleHandlerCommandHandlerExampleResponseResponseroutesparsed byroutes toparses toroutesroutes toexecuted bycreatesparses toexecuted byproduces \ No newline at end of file diff --git a/documents/images/docs/networking/commands/request.puml b/documents/images/docs/networking/commands/request.puml new file mode 100644 index 0000000..9e27697 --- /dev/null +++ b/documents/images/docs/networking/commands/request.puml @@ -0,0 +1,11 @@ +@startuml +skinparam backgroundColor transparent + +abstract class Request { + # context: RequestContext + + getContext(): RequestContext + + getSessionId(): SessionId + + getRequestId(): int +} + +@enduml diff --git a/documents/images/docs/networking/commands/request.svg b/documents/images/docs/networking/commands/request.svg new file mode 100644 index 0000000..c4d8826 --- /dev/null +++ b/documents/images/docs/networking/commands/request.svg @@ -0,0 +1 @@ +Requestcontext: RequestContextgetContext(): RequestContextgetSessionId(): SessionIdgetRequestId(): int \ No newline at end of file diff --git a/documents/images/docs/networking/commands/request_parameter_accessor.puml b/documents/images/docs/networking/commands/request_parameter_accessor.puml new file mode 100644 index 0000000..4c5419b --- /dev/null +++ b/documents/images/docs/networking/commands/request_parameter_accessor.puml @@ -0,0 +1,14 @@ +@startuml +skinparam backgroundColor transparent + +class RequestParameterAccessor { + - index: Map + + + RequestParameterAccessor(parameters: List) + + require(key: String): String + + require(key: String, parser: ThrowingParser): T + + optional(key: String, defaultValue: String): String + + optional(key: String, defaultValue: T, parser: ThrowingParser): T +} + +@enduml diff --git a/documents/images/docs/networking/commands/request_parameter_accessor.svg b/documents/images/docs/networking/commands/request_parameter_accessor.svg new file mode 100644 index 0000000..d9a6bce --- /dev/null +++ b/documents/images/docs/networking/commands/request_parameter_accessor.svg @@ -0,0 +1 @@ +RequestParameterAccessorindex: Map<String, String>RequestParameterAccessor(parameters: List<RequestParameters>)require(key: String): Stringrequire(key: String, parser: ThrowingParser<T>): Toptional(key: String, defaultValue: String): Stringoptional(key: String, defaultValue: T, parser: ThrowingParser<T>): T \ No newline at end of file diff --git a/documents/images/docs/networking/commands/response_types.puml b/documents/images/docs/networking/commands/response_types.puml new file mode 100644 index 0000000..51d5733 --- /dev/null +++ b/documents/images/docs/networking/commands/response_types.puml @@ -0,0 +1,23 @@ +@startuml +skinparam backgroundColor transparent + +abstract class Response { + # context: RequestContext + # body: ResponseBody + + prefix(): String + + getSessionId(): SessionId + + getRequestId(): int + + getBody(): ResponseBody +} + +abstract class SuccessResponse extends Response { + + prefix(): String (+OK) +} + +class OkResponse extends SuccessResponse + +class ErrorResponse extends Response { + + prefix(): String (-ERR) +} + +@enduml diff --git a/documents/images/docs/networking/commands/response_types.svg b/documents/images/docs/networking/commands/response_types.svg new file mode 100644 index 0000000..142696c --- /dev/null +++ b/documents/images/docs/networking/commands/response_types.svg @@ -0,0 +1 @@ +Responsecontext: RequestContextbody: ResponseBodyprefix(): StringgetSessionId(): SessionIdgetRequestId(): intgetBody(): ResponseBodySuccessResponseprefix(): String (+OK)OkResponseErrorResponseprefix(): String (-ERR) \ No newline at end of file diff --git a/documents/images/docs/networking/commands/sequence_diagram.puml b/documents/images/docs/networking/commands/sequence_diagram.puml new file mode 100644 index 0000000..ee932eb --- /dev/null +++ b/documents/images/docs/networking/commands/sequence_diagram.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent + +participant SessionReader +participant CommandParserDispatcher as Dispatcher +participant "CommandParser" as Parser +participant CommandRouter as Router +participant "CommandHandler" as Handler +participant ResponseDispatcher + +SessionReader -> Dispatcher : parse(primitiveRequest) +Dispatcher -> Parser : parse(primitiveRequest) +Parser --> Dispatcher : ExampleRequest +Dispatcher --> SessionReader : Request + +SessionReader -> Router : execute(request) +Router -> Handler : execute(ExampleRequest) +Handler -> ResponseDispatcher : dispatch(ExampleRequest) + +@enduml diff --git a/documents/images/docs/networking/commands/sequence_diagram.svg b/documents/images/docs/networking/commands/sequence_diagram.svg new file mode 100644 index 0000000..b838d09 --- /dev/null +++ b/documents/images/docs/networking/commands/sequence_diagram.svg @@ -0,0 +1 @@ +SessionReaderCommandParserDispatcherCommandParser.T.CommandRouterCommandHandler.T.ResponseDispatcherSessionReaderSessionReaderCommandParserDispatcherCommandParserDispatcherCommandParser<T>CommandParser<T>CommandRouterCommandRouterCommandHandler<T>CommandHandler<T>ResponseDispatcherResponseDispatcherparse(primitiveRequest)parse(primitiveRequest)ExampleRequestRequestexecute(request)execute(ExampleRequest)dispatch(ExampleRequest) \ No newline at end of file diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java index 7fa4291..d5e2e2a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java @@ -10,15 +10,15 @@ import org.apache.logging.log4j.Logger; /** * Entry point for the Casono client application. Handles client startup and connection parameters. * - *

Standardkonstruktor für die Anwendung. + *

Default constructor for the application. */ public class ClientApp { private static final Logger LOGGER = LogManager.getLogger(ClientApp.class); - /** Standardkonstruktor. */ + /** Default constructor. */ public ClientApp() { - // Standardkonstruktor + // Default constructor } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/Launcher.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/Launcher.java index f4d4069..85d42df 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/Launcher.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/Launcher.java @@ -6,13 +6,13 @@ import javafx.application.Application; /** * Launcher for the Casono main UI. * - *

Standardkonstruktor für die Anwendung. + *

Default constructor for the application. */ public class Launcher { - /** Standardkonstruktor. */ + /** Default constructor. */ public Launcher() { - // Standardkonstruktor + // Default constructor } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatController.java index b6b273f..1f7abe3 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatController.java @@ -41,6 +41,11 @@ public class ChatController { chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty()); } + /** Standardkonstruktor. Wird von FXML verwendet. */ + public ChatController() { + // default constructor for FXML + } + /** * Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an * das Netzwerkprotokoll weiter. diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java index c96c9bd..ae52485 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java @@ -15,11 +15,22 @@ import javafx.scene.layout.VBox; */ public class CasinoGameController { + /** Standardkonstruktor. Wird von FXML verwendet. */ + public CasinoGameController() { + // default constructor for FXML + } + @FXML private Label welcomeText; @FXML private VBox casinoTable; // TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt, // sobald die GameEngine fertig ist + + /** + * Temporäre Test-Methode, die bei Klick auf den Tisch eine Platzhalteraktion ausführt. + * + *

Wird in der finalen Implementierung durch die Spiel-Logik ersetzt. + */ @FXML public void onTableClick() { welcomeText.setText("Einsatz akzeptiert!"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java index f9b5419..5083ee6 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java @@ -17,6 +17,11 @@ import javafx.stage.Stage; */ public class CasinoGameUI extends Application { + /** Standardkonstruktor. */ + public CasinoGameUI() { + // default no-arg constructor + } + private static final int DEFAULT_WIDTH = 1200; private static final int DEFAULT_HEIGHT = 800; @@ -29,7 +34,7 @@ public class CasinoGameUI extends Application { @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = - new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/casinogameui.fxml")); + new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/Casinogameui.fxml")); Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT); stage.setTitle("Casono (GAME)"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java index e351b7a..03c4644 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java @@ -27,6 +27,8 @@ import javafx.scene.shape.Rectangle; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Experimenteller integrierter Browser für Casono. @@ -50,13 +52,17 @@ import javafx.stage.Stage; */ public class CasinoBrowserController { + /** Standardkonstruktor. Initialisiert den CasinoBrowserController. */ + public CasinoBrowserController() { + // Intentionally left blank; controller initialization is FXML-driven. + } + private static final Set TRUSTED_DOMAINS = new HashSet<>(); private static final CookieManager COOKIE_MANAGER = new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER); - private static final org.apache.logging.log4j.Logger LOGGER = - org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class); + private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class); private static final int LOGO_HEIGHT = 40; private static final int CORNER_RADIUS = 40; @@ -354,8 +360,9 @@ public class CasinoBrowserController { fwdBtn.getStyleClass().add("gray-button"); fwdBtn.setOnAction( e -> { - if (engine.getHistory().getCurrentIndex() - < engine.getHistory().getEntries().size() - 1) { + int currentIndex = engine.getHistory().getCurrentIndex(); + int lastIndex = engine.getHistory().getEntries().size() - 1; + if (currentIndex < lastIndex) { engine.getHistory().go(1); } }); @@ -423,7 +430,7 @@ public class CasinoBrowserController { Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT); - var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css"); + var css = CasinoBrowserController.class.getResource("/ui-structure/Casinogameui.css"); if (css != null) { scene.getStylesheets().add(css.toExternalForm()); @@ -512,10 +519,11 @@ public class CasinoBrowserController { alert.setTitle("Unbekannte Website"); alert.setHeaderText("Diese Website ist nicht bekannt"); - alert.setContentText( + String content = host - + "\n\nDiese Seite ist nicht vom " - + "Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?"); + + "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\n" + + "Möchten Sie sie trotzdem öffnen?"; + alert.setContentText(content); var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH); Image logo = new Image(stream); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java index 33e1e32..b446f3a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java @@ -1,12 +1,14 @@ package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents; -import javafx.application.Platform; +import ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * Controller für die interaktive Taskleiste innerhalb der Poker-UI. @@ -16,8 +18,12 @@ import javafx.scene.layout.HBox; */ public class TaskbarController { - private static final org.apache.logging.log4j.Logger LOGGER = - org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class); + /** Standardkonstruktor. Wird von FXML verwendet. */ + public TaskbarController() { + // default constructor for FXML + } + + private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class); @FXML private HBox taskbar; @FXML private TextField taskbarInput; @@ -92,15 +98,21 @@ public class TaskbarController { processBet(); } - /** - * Wird aufgerufen, wenn der Exit-Button in der Taskleiste gedrückt wird. - * - *

TODO: Logik implementieren, um zur Lobby zurückzukehren, ohne die gesamte Anwendung zu - * schließen (kein System.exit/Platform.exit). - */ @FXML private void onExitButtonClick() { - Platform.exit(); + javafx.application.Platform.runLater( + () -> { + // Close game stage + javafx.stage.Stage currentStage = + (javafx.stage.Stage) taskbar.getScene().getWindow(); + currentStage.close(); + // Lobby-UI starten + try { + new Casinomainui().start(new javafx.stage.Stage()); + } catch (Exception e) { + LOGGER.error("Fehler beim Starten der Lobby-UI: {}", e.getMessage()); + } + }); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/Casinomainui.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/Casinomainui.java index 959c8d3..0088781 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/Casinomainui.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/Casinomainui.java @@ -10,13 +10,13 @@ import javafx.stage.Stage; /** * JavaFX Application class for the Casono main UI. * - *

Standardkonstruktor für die Anwendung. + *

Default constructor for the application. */ public class Casinomainui extends Application { - /** Standardkonstruktor. */ + /** Default constructor. */ public Casinomainui() { - // Standardkonstruktor + // Default constructor } private static final int SCENE_WIDTH = 1200; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java index 56fe07e..c82a381 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java @@ -28,6 +28,7 @@ public class CasinomainuiController { private LobbyButtonGridManager gridManager; private int nextButtonId = 1; + /** Default constructor for dependency injection by FXMLLoader. */ public CasinomainuiController() { // Default constructor } @@ -39,7 +40,7 @@ public class CasinomainuiController { subtitleLabel.setText("Texas Hold'em Poker"); logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm())); - translationManager = new LobbyButtonTranslationManager(); + translationManager = LobbyButtonTranslationManager.getInstance(); gridManager = new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager); casinoTable.getChildren().clear(); @@ -57,7 +58,7 @@ public class CasinomainuiController { @FXML public void handleCreateLobbyButton() { if (translationManager.isFull()) { - LOGGER.warn("Grid voll! Keine weiteren Lobbys moeglich."); + LOGGER.warn("Grid is full! No more lobbies available."); return; } int buttonId = nextButtonId++; @@ -67,7 +68,7 @@ public class CasinomainuiController { LOGGER.info("ButtonID: {}, LobbyID: {}", buttonId, lobbyId); gridManager.renderLobbyButtons(); } catch (Exception e) { - LOGGER.error("Fehler beim Hinzufügen: {}", e.getMessage()); + LOGGER.error("Error while adding lobby button: {}", e.getMessage()); } } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java index 03259a7..3f75b0f 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java @@ -17,6 +17,8 @@ import org.apache.logging.log4j.Logger; * ButtonID to LobbyID. */ public class LobbyButtonGridManager { + private static final double BUTTON_WIDTH_MARGIN = 20.0; + private static final double BUTTON_MIN_SIZE = 10.0; private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class); /** GridPane for the button grid. */ @@ -46,7 +48,8 @@ public class LobbyButtonGridManager { public LobbyButtonGridManager( GridPane gridPane, LobbyButtonTranslationManager translationManager) { this.gridPane = gridPane; - this.translationManager = translationManager; + // Singleton immer verwenden + this.translationManager = LobbyButtonTranslationManager.getInstance(); } /** @@ -65,8 +68,21 @@ public class LobbyButtonGridManager { int buttonId = entry.getKey(); Button btn = new Button(); btn.setId("lobbyBtn-" + buttonId); - btn.setGraphic( - new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH)))); + ImageView imageView = + new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH))); + imageView.setPreserveRatio(true); + // Dynamische Breite: Bindung an die Zellengröße + imageView + .fitWidthProperty() + .bind(gridPane.widthProperty().divide(COLS).subtract(BUTTON_WIDTH_MARGIN)); + imageView.setSmooth(true); + btn.setGraphic(imageView); + btn.setMaxWidth(Double.MAX_VALUE); + btn.setMaxHeight(Double.MAX_VALUE); + btn.setMinWidth(BUTTON_MIN_SIZE); + btn.setMinHeight(BUTTON_MIN_SIZE); + GridPane.setHgrow(btn, javafx.scene.layout.Priority.ALWAYS); + GridPane.setVgrow(btn, javafx.scene.layout.Priority.ALWAYS); btn.setOnAction( e -> { Integer lobbyId = translationManager.getLobbyIdForButton(buttonId); @@ -99,8 +115,22 @@ public class LobbyButtonGridManager { * @param lobbyId The lobbyId to join */ public void joinLobby(int lobbyId) { - // TODO: Replace with actual join logic + // Game-UI starten und Lobby-UI schließen LOGGER.info("Joining lobby: {}", lobbyId); + javafx.application.Platform.runLater( + () -> { + // Lobby-Stage schließen + javafx.stage.Stage currentStage = + (javafx.stage.Stage) gridPane.getScene().getWindow(); + currentStage.close(); + // Game-UI starten + try { + new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI() + .start(new javafx.stage.Stage()); + } catch (Exception e) { + LOGGER.error("Fehler beim Starten der Game-UI: {}", e.getMessage()); + } + }); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManager.java index 19af19e..967fe83 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManager.java @@ -4,36 +4,53 @@ import java.util.HashMap; import java.util.Map; /** - * Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher. Keine Dateioperationen, - * nur Laufzeitdatenstruktur. + * Manages the mapping between Button IDs and Lobby IDs in memory only. No file operations, + * runtime-only data structure. */ public class LobbyButtonTranslationManager { - /** Maximale Anzahl an Buttons/Lobbys */ + + // Singleton instance + private static LobbyButtonTranslationManager instance; + + // Singleton access + /** + * Returns the singleton instance of the manager. + * + * @return the single instance of {@code LobbyButtonTranslationManager} + */ + public static LobbyButtonTranslationManager getInstance() { + if (instance == null) { + instance = new LobbyButtonTranslationManager(); + } + return instance; + } + + /** Maximum number of buttons/lobbies */ private static final int MAX_BUTTONS = 8; - /** Zuordnung ButtonID → LobbyID */ + /** Mapping ButtonID → LobbyID */ private final Map buttonIdToLobbyId = new HashMap<>(); - /** Konstruktor: initialisiert die Zuordnung leer. */ - public LobbyButtonTranslationManager() { - // Zuordnung bleibt leer beim Start + /** Private constructor for the singleton pattern */ + private LobbyButtonTranslationManager() { + // Mapping is empty at startup } /** - * Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht). + * Checks whether the grid is full (MAX_BUTTONS reached). * - * @return true, wenn Grid voll; sonst false + * @return true if the grid is full; otherwise false */ public boolean isFull() { return buttonIdToLobbyId.size() >= MAX_BUTTONS; } /** - * Fügt eine Zuordnung ButtonID → LobbyID hinzu. + * Adds a mapping ButtonID → LobbyID. * - * @param buttonId Die ID des Buttons - * @param lobbyId Die ID der Lobby - * @throws Exception wenn das Grid voll ist + * @param buttonId the ID of the button + * @param lobbyId the ID of the lobby + * @throws Exception when the grid is full */ public void addLobbyButton(int buttonId, int lobbyId) throws Exception { if (isFull()) { @@ -43,28 +60,28 @@ public class LobbyButtonTranslationManager { } /** - * Entfernt eine Zuordnung für die gegebene ButtonID. + * Removes the mapping for the given ButtonID. * - * @param buttonId Die ID des zu entfernenden Buttons + * @param buttonId the ID of the button to remove */ public void removeLobbyButton(int buttonId) { buttonIdToLobbyId.remove(buttonId); } /** - * Gibt die LobbyID für eine gegebene ButtonID zurück. + * Returns the LobbyID for a given ButtonID. * - * @param buttonId Die ButtonID - * @return Die zugehoerige LobbyID oder null, falls nicht vorhanden + * @param buttonId the ButtonID + * @return the associated LobbyID or null if not present */ public Integer getLobbyIdForButton(int buttonId) { return buttonIdToLobbyId.get(buttonId); } /** - * Gibt die gesamte Zuordnung ButtonID → LobbyID zurück. + * Returns the full mapping ButtonID → LobbyID. * - * @return Map aller Zuordnungen + * @return Map of all mappings */ public Map getButtonIdToLobbyId() { return buttonIdToLobbyId; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java index 5687b5b..48ac98f 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java @@ -1,5 +1,11 @@ package ch.unibas.dmi.dbis.cs108.casono.server; +import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameParser; +import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameRequest; +import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingParser; +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.UserRegistry; import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager; @@ -7,6 +13,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandR 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.EventBus; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher; import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob; import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager; import java.time.Duration; @@ -58,6 +65,31 @@ public class ServerApp { SESSION_DISCONNECT_JOB_PERIOD, TimeUnit.SECONDS); + ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager); + + registerCommands(dispatcher, router, responseDispatcher, userRegistry); + networkManager.start(); } + + /** + * Registers command parsers and handlers. + * + * @param parserDispatcher the dispatcher responsible for parsing incoming commands + * @param commandRouter the router that dispatches parsed commands to appropriate handlers + * @param responseDispatcher the dispatcher responsible for sending responses back to clients + */ + private static void registerCommands( + CommandParserDispatcher parserDispatcher, + CommandRouter commandRouter, + ResponseDispatcher responseDispatcher, + UserRegistry userRegistry) { + parserDispatcher.register("PING", new PingParser()); + commandRouter.register(PingRequest.class, new PingHandler(responseDispatcher)); + + parserDispatcher.register("CHECK_USERNAME", new CheckUsernameParser()); + commandRouter.register( + CheckUsernameRequest.class, + new CheckUsernameHandler(responseDispatcher, userRegistry)); + } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameHandler.java new file mode 100644 index 0000000..c238ba1 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameHandler.java @@ -0,0 +1,44 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick; + +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.CommandHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher; +import java.util.Optional; + +/** Handles {@link CheckUsernameRequest}s to check whether a username is available. */ +public class CheckUsernameHandler implements CommandHandler { + private final ResponseDispatcher responseDispatcher; + private final UserRegistry userRegistry; + + /** + * Creates a new handler for checking username availability. + * + * @param responseDispatcher the dispatcher used to send the response + * @param userRegistry the registry used to look up existing users + */ + public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) { + this.responseDispatcher = responseDispatcher; + this.userRegistry = userRegistry; + } + + /** + * Executes the username availability check for the given request. + * + *

If no user exists for the requested username, the username is reported as {@link + * UsernameAvailability#FREE}; otherwise, it is reported as {@link UsernameAvailability#TAKEN}. + * + * @param request the request to execute + */ + @Override + public void execute(CheckUsernameRequest request) { + Optional user = userRegistry.getByUsername(request.getUsername()); + UsernameAvailability availability; + if (user.isEmpty()) { + availability = UsernameAvailability.FREE; + } else { + availability = UsernameAvailability.TAKEN; + } + responseDispatcher.dispatch(new CheckUsernameResponse(request.getContext(), availability)); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameParser.java new file mode 100644 index 0000000..20624c0 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameParser.java @@ -0,0 +1,21 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor; + +/** Parses a primitive request into a {@link CheckUsernameRequest}. */ +public class CheckUsernameParser implements CommandParser { + /** + * Extracts the required {@code USERNAME} parameter from the incoming request. + * + * @param primitiveRequest the request to parse + * @return {@link CheckUsernameRequest} containing the username + */ + @Override + public CheckUsernameRequest parse(PrimitiveRequest primitiveRequest) { + RequestParameterAccessor accessor = + new RequestParameterAccessor(primitiveRequest.parameters()); + return new CheckUsernameRequest(primitiveRequest.context(), accessor.require("USERNAME")); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameRequest.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameRequest.java new file mode 100644 index 0000000..7247b0e --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameRequest.java @@ -0,0 +1,30 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; + +/** Request implementation used to check whether a username is available or already taken */ +public class CheckUsernameRequest extends Request { + private final String username; + + /** + * Constructs a new CheckUsernameRequest with the given context and username to check + * + * @param context the {@link RequestContext} containing information for responding to the + * request + * @param username the username to check for availability + */ + public CheckUsernameRequest(RequestContext context, String username) { + super(context); + this.username = username; + } + + /** + * Returns the provided username in the request + * + * @return username to check + */ + public String getUsername() { + return username; + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameResponse.java new file mode 100644 index 0000000..5018323 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/CheckUsernameResponse.java @@ -0,0 +1,18 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder; + +/** Response indicating the availability status of a username check. */ +public class CheckUsernameResponse extends SuccessResponse { + /** + * Creates a new response to respond to the username availability check to + * + * @param context the {@link RequestContext} associated with the request + * @param availability the availability status of the requested username + */ + public CheckUsernameResponse(RequestContext context, UsernameAvailability availability) { + super(context, new ResponseBodyBuilder().param("STATUS", availability).build()); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/UsernameAvailability.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/UsernameAvailability.java new file mode 100644 index 0000000..df3d358 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/check_nick/UsernameAvailability.java @@ -0,0 +1,10 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick; + +/** Represents the availability status of a username */ +enum UsernameAvailability { + /** Username is available */ + FREE, + + /** Username is already in use */ + TAKEN +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingHandler.java new file mode 100644 index 0000000..1669cd8 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingHandler.java @@ -0,0 +1,29 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher; + +/** Handler for {@link PingRequest}. */ +public class PingHandler implements CommandHandler { + private final ResponseDispatcher responseDispatcher; + + /** + * Create a new PingHandler to execute {@link PingRequest}s + * + * @param responseDispatcher dispatcher used to send responses back to clients + */ + public PingHandler(ResponseDispatcher responseDispatcher) { + this.responseDispatcher = responseDispatcher; + } + + /** + * Execute the ping request. + * + * @param request the ping request to handle + */ + @Override + public void execute(PingRequest request) { + responseDispatcher.dispatch(new OkResponse(request.getContext())); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingParser.java new file mode 100644 index 0000000..69ab980 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingParser.java @@ -0,0 +1,22 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest; + +/** + * Parser for the Ping command. + * + *

Converts a low-level {@link PrimitiveRequest} into a {@link PingRequest}. + */ +public class PingParser implements CommandParser { + /** + * Parse the given primitive request into a {@link PingRequest}. + * + * @param primitiveRequest the raw request to parse + * @return {@link PingRequest} + */ + @Override + public PingRequest parse(PrimitiveRequest primitiveRequest) { + return new PingRequest(primitiveRequest.context()); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingRequest.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingRequest.java new file mode 100644 index 0000000..25ab93f --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/ping/PingRequest.java @@ -0,0 +1,19 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping; + +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; + +/** + * Represents a "PING" request sent by a client to check server availability and keep the connection + * alive. + */ +public class PingRequest extends Request { + /** + * Constructs a new PingRequest with the given context. + * + * @param context the request context associated with this request + */ + public PingRequest(RequestContext context) { + super(context); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserRegistry.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserRegistry.java index adb1abe..bc0cd36 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserRegistry.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserRegistry.java @@ -86,15 +86,38 @@ public class UserRegistry { } /** - * Looks up a user by their session ID. + * Looks up a user by their {@link SessionId}. * - * @param sessionId the session ID to look up - * @return an Optional containing the user, or empty if no user is associated with this session + * @param sessionId the SessionId to look up + * @return an Optional containing the {@link User}, or empty if no user is associated with this + * SessionId */ - public Optional findBySessionId(SessionId sessionId) { + public Optional getBySessionId(SessionId sessionId) { return Optional.ofNullable(bySessionId.get(sessionId)); } + /** + * Looks up a user by their {@link UserId}. + * + * @param userId the UserId to look up + * @return an Optional containing the {@link User}, or empty if no user is associated with this + * UserId + */ + public Optional getByUserId(UserId userId) { + return Optional.ofNullable(byId.get(userId)); + } + + /** + * Looks up a user by their username. + * + * @param username the username to look up + * @return an Optional containing the {@link User}, or empty if no user is associated with this + * username + */ + public Optional getByUsername(String username) { + return Optional.ofNullable(byName.get(username)); + } + /** * Returns all currently registered users. * diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/command/parsing/CommandParser.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/command/parsing/CommandParser.java index 3a3b76f..7a777e3 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/command/parsing/CommandParser.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/command/parsing/CommandParser.java @@ -7,12 +7,12 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request; * Parser to convert the PrimitiveRequest to a Request and performing checks for required fields and * data types */ -public interface CommandParser { +public interface CommandParser { /** * Parses the provided PrimitiveRequest into a command-specific request * * @param primitiveRequest * @return */ - Request parse(PrimitiveRequest primitiveRequest); + T parse(PrimitiveRequest primitiveRequest); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/ErrorResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/ErrorResponse.java index 1136e11..44c9aab 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/ErrorResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/ErrorResponse.java @@ -1,23 +1,20 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; -import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId; /** Response representing an error outcome for a client's request. */ public class ErrorResponse extends Response { /** * Construct an error response with a code and message. * - * @param sessionId the target session id - * @param requestId the originating request id + * @param context the RequestContext of the request * @param errorCode a short error code identifying the failure * @param errorMessage a human readable error message */ - public ErrorResponse( - SessionId sessionId, int requestId, String errorCode, String errorMessage) { + public ErrorResponse(RequestContext context, String errorCode, String errorMessage) { super( - sessionId, - requestId, + context, ResponseBody.builder().param("CODE", errorCode).param("MSG", errorMessage).build()); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/OkResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/OkResponse.java index f0d7cd6..cc5526c 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/OkResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/OkResponse.java @@ -1,7 +1,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; -import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId; /** * A simple success response with an empty body. @@ -12,10 +12,9 @@ public class OkResponse extends SuccessResponse { /** * Create a minimal successful response (no body content). * - * @param sessionId the target session id - * @param requestId the originating request id + * @param context the RequestContext of the request */ - public OkResponse(SessionId sessionId, int requestId) { - super(sessionId, requestId, ResponseBody.builder().build()); + public OkResponse(RequestContext context) { + super(context, ResponseBody.builder().build()); } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/Response.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/Response.java index d0918fb..13d7ad9 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/Response.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/Response.java @@ -1,24 +1,22 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId; /** Abstract base class for all server responses sent to clients. */ public abstract class Response { - private final SessionId sessionId; - private final int requestId; + private final RequestContext context; private final ResponseBody body; /** * Create a new {@code Response}. * - * @param sessionId the id of the session this response targets - * @param requestId the request identifier this response corresponds to + * @param context the RequestContext of the request * @param body the structured response body */ - protected Response(SessionId sessionId, int requestId, ResponseBody body) { - this.sessionId = sessionId; - this.requestId = requestId; + protected Response(RequestContext context, ResponseBody body) { + this.context = context; this.body = body; } @@ -30,12 +28,12 @@ public abstract class Response { public abstract String prefix(); /** - * Returns the session id that should receive this response. + * Returns the session id of the session that should receive this response. * * @return the target {@link SessionId} */ public SessionId getSessionId() { - return sessionId; + return context.sessionId(); } /** @@ -44,7 +42,7 @@ public abstract class Response { * @return the numeric request id */ public int getRequestId() { - return requestId; + return context.requestId(); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/SuccessResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/SuccessResponse.java index 3df95b9..9b96860 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/SuccessResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/SuccessResponse.java @@ -1,7 +1,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; -import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId; /** * Abstract {@link Response} specialization indicating a successful outcome. @@ -13,12 +13,11 @@ public abstract class SuccessResponse extends Response { /** * Create a successful response with the provided body. * - * @param sessionId the session id this response targets - * @param requestId the originating request id + * @param context the RequestContext of the request * @param body the response body */ - protected SuccessResponse(SessionId sessionId, int requestId, ResponseBody body) { - super(sessionId, requestId, body); + protected SuccessResponse(RequestContext context, ResponseBody body) { + super(context, body); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatchException.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatchException.java new file mode 100644 index 0000000..6cf4010 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatchException.java @@ -0,0 +1,7 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher; + +public class ResponseDispatchException extends RuntimeException { + public ResponseDispatchException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatcher.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatcher.java index 1be2f27..1895d44 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatcher.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/response/dispatcher/ResponseDispatcher.java @@ -27,12 +27,17 @@ public class ResponseDispatcher { * the target session's response queue. * * @param response the response to dispatch - * @throws InterruptedException if the thread is interrupted while waiting to enqueue the - * primitive response + * @throws ResponseDispatchException wraps any exceptions that occur during dispatching, such as + * the {@link InterruptedException} */ - public void dispatch(Response response) throws InterruptedException { + public void dispatch(Response response) { PrimitiveResponse primitiveResponse = ResponseEncoder.encode(response); Session session = sessionManager.getSessionById(response.getSessionId()); - session.getResponseQueue().put(primitiveResponse); + try { + session.getResponseQueue().put(primitiveResponse); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ResponseDispatchException("Interrupted while dispatching response", e); + } } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java index d7f6cdf..78ffbfd 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java @@ -12,8 +12,10 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Primitive import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.MissingParameterException; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse; +import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatchException; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseEncoder; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer; @@ -46,11 +48,13 @@ public class SessionReader implements Runnable { while (!Thread.currentThread().isInterrupted()) { RawPacket rawPacket = null; RawRequest rawRequest = null; + RequestContext requestContext = null; try { // Step 1: Read from transport rawPacket = transport.read(); session.updateLastInboundActivity(); logger.debug("Recieved: {}", rawPacket); + requestContext = new RequestContext(session.getId(), rawPacket.requestId()); // Step 2: Syntax validation and conversion into transport object rawRequest = ProtocolParser.parse(rawPacket.payload()); @@ -58,9 +62,7 @@ public class SessionReader implements Runnable { PrimitiveRequest primitiveRequest = new PrimitiveRequest( - new RequestContext(session.getId(), rawPacket.requestId()), - rawRequest.command(), - rawRequest.parameters()); + requestContext, rawRequest.command(), rawRequest.parameters()); logger.debug("Converted to {}", primitiveRequest); // Step 3: Parse into Request and execute Request @@ -76,8 +78,7 @@ public class SessionReader implements Runnable { sendErrorResponse( new ErrorResponse( - session.getId(), - rawPacket.requestId(), + requestContext, "PARSING_ERROR", "Error occured during parsing. Likely due to malformed payload.")); @@ -85,11 +86,23 @@ public class SessionReader implements Runnable { logger.error("Recieved unknown command '{}' from client", rawRequest.command(), e); sendErrorResponse( new ErrorResponse( - session.getId(), - rawPacket.requestId(), + requestContext, "UNKNOWN_COMMAND", "This command is unknown to the server.")); + } catch (ResponseDispatchException e) { + logger.error( + "Unexpected ResponseDispatchException exception while dispatching request", + e); + + } catch (MissingParameterException e) { + logger.error( + "Recieved request for command '{}' was missing the '{}' parameter", + rawRequest.command(), + e.getParameterKey()); + sendErrorResponse( + new ErrorResponse(requestContext, "MISSING_PARAMETER", e.getMessage())); + } catch (IOException e) { logger.error("Unexpected IO exception while reading from transport", e); @@ -97,8 +110,7 @@ public class SessionReader implements Runnable { logger.error("Unexpected RuntimeException occured", e); sendErrorResponse( new ErrorResponse( - session.getId(), - rawPacket.requestId(), + requestContext, "INTERNAL_ERROR", "Unexpected internal server error occured.")); } diff --git a/src/main/resources/images/background-3.png b/src/main/resources/images/background-3.png new file mode 100644 index 0000000..a299281 Binary files /dev/null and b/src/main/resources/images/background-3.png differ diff --git a/src/main/resources/images/background-4.png b/src/main/resources/images/background-4.png new file mode 100644 index 0000000..19b9edb Binary files /dev/null and b/src/main/resources/images/background-4.png differ diff --git a/src/main/resources/images/blue-background-1.png b/src/main/resources/images/blue-background-1.png new file mode 100644 index 0000000..52d497d Binary files /dev/null and b/src/main/resources/images/blue-background-1.png differ diff --git a/src/main/resources/images/card-cross-1-1.png b/src/main/resources/images/card-cross-1-1.png new file mode 100644 index 0000000..dccdaa5 Binary files /dev/null and b/src/main/resources/images/card-cross-1-1.png differ diff --git a/src/main/resources/images/card-cross-1-3.png b/src/main/resources/images/card-cross-1-3.png new file mode 100644 index 0000000..9724b1e Binary files /dev/null and b/src/main/resources/images/card-cross-1-3.png differ diff --git a/src/main/resources/images/card-cross-10-3.png b/src/main/resources/images/card-cross-10-3.png new file mode 100644 index 0000000..f13385c Binary files /dev/null and b/src/main/resources/images/card-cross-10-3.png differ diff --git a/src/main/resources/images/card-cross-2-3.png b/src/main/resources/images/card-cross-2-3.png new file mode 100644 index 0000000..acf3262 Binary files /dev/null and b/src/main/resources/images/card-cross-2-3.png differ diff --git a/src/main/resources/images/card-cross-3-3.png b/src/main/resources/images/card-cross-3-3.png new file mode 100644 index 0000000..20fffc1 Binary files /dev/null and b/src/main/resources/images/card-cross-3-3.png differ diff --git a/src/main/resources/images/card-cross-4-3.png b/src/main/resources/images/card-cross-4-3.png new file mode 100644 index 0000000..be35620 Binary files /dev/null and b/src/main/resources/images/card-cross-4-3.png differ diff --git a/src/main/resources/images/card-cross-5-3.png b/src/main/resources/images/card-cross-5-3.png new file mode 100644 index 0000000..b053703 Binary files /dev/null and b/src/main/resources/images/card-cross-5-3.png differ diff --git a/src/main/resources/images/card-cross-6-3.png b/src/main/resources/images/card-cross-6-3.png new file mode 100644 index 0000000..2f6429b Binary files /dev/null and b/src/main/resources/images/card-cross-6-3.png differ diff --git a/src/main/resources/images/card-cross-7-3.png b/src/main/resources/images/card-cross-7-3.png new file mode 100644 index 0000000..c950802 Binary files /dev/null and b/src/main/resources/images/card-cross-7-3.png differ diff --git a/src/main/resources/images/card-cross-8-3.png b/src/main/resources/images/card-cross-8-3.png new file mode 100644 index 0000000..5fc5f58 Binary files /dev/null and b/src/main/resources/images/card-cross-8-3.png differ diff --git a/src/main/resources/images/card-cross-9-3.png b/src/main/resources/images/card-cross-9-3.png new file mode 100644 index 0000000..41d317f Binary files /dev/null and b/src/main/resources/images/card-cross-9-3.png differ diff --git a/src/main/resources/images/card-cross-ace-3.png b/src/main/resources/images/card-cross-ace-3.png new file mode 100644 index 0000000..1eb1eda Binary files /dev/null and b/src/main/resources/images/card-cross-ace-3.png differ diff --git a/src/main/resources/images/card-cross-jack-3.png b/src/main/resources/images/card-cross-jack-3.png new file mode 100644 index 0000000..d81adf0 Binary files /dev/null and b/src/main/resources/images/card-cross-jack-3.png differ diff --git a/src/main/resources/images/card-cross-king-3.png b/src/main/resources/images/card-cross-king-3.png new file mode 100644 index 0000000..2e7b58e Binary files /dev/null and b/src/main/resources/images/card-cross-king-3.png differ diff --git a/src/main/resources/images/card-cross-queen-3.png b/src/main/resources/images/card-cross-queen-3.png new file mode 100644 index 0000000..1aa81a2 Binary files /dev/null and b/src/main/resources/images/card-cross-queen-3.png differ diff --git a/src/main/resources/images/card-diamond-1-3.png b/src/main/resources/images/card-diamond-1-3.png new file mode 100644 index 0000000..a9ac4aa Binary files /dev/null and b/src/main/resources/images/card-diamond-1-3.png differ diff --git a/src/main/resources/images/card-diamond-10-3.png b/src/main/resources/images/card-diamond-10-3.png new file mode 100644 index 0000000..989f685 Binary files /dev/null and b/src/main/resources/images/card-diamond-10-3.png differ diff --git a/src/main/resources/images/card-diamond-2-3.png b/src/main/resources/images/card-diamond-2-3.png new file mode 100644 index 0000000..c79670f Binary files /dev/null and b/src/main/resources/images/card-diamond-2-3.png differ diff --git a/src/main/resources/images/card-diamond-3-3.png b/src/main/resources/images/card-diamond-3-3.png new file mode 100644 index 0000000..e980945 Binary files /dev/null and b/src/main/resources/images/card-diamond-3-3.png differ diff --git a/src/main/resources/images/card-diamond-4-3.png b/src/main/resources/images/card-diamond-4-3.png new file mode 100644 index 0000000..c71c59d Binary files /dev/null and b/src/main/resources/images/card-diamond-4-3.png differ diff --git a/src/main/resources/images/card-diamond-5-3.png b/src/main/resources/images/card-diamond-5-3.png new file mode 100644 index 0000000..f91de3a Binary files /dev/null and b/src/main/resources/images/card-diamond-5-3.png differ diff --git a/src/main/resources/images/card-diamond-6-3.png b/src/main/resources/images/card-diamond-6-3.png new file mode 100644 index 0000000..6dd1156 Binary files /dev/null and b/src/main/resources/images/card-diamond-6-3.png differ diff --git a/src/main/resources/images/card-diamond-7-3.png b/src/main/resources/images/card-diamond-7-3.png new file mode 100644 index 0000000..79e9392 Binary files /dev/null and b/src/main/resources/images/card-diamond-7-3.png differ diff --git a/src/main/resources/images/card-diamond-8-3.png b/src/main/resources/images/card-diamond-8-3.png new file mode 100644 index 0000000..6f1ac39 Binary files /dev/null and b/src/main/resources/images/card-diamond-8-3.png differ diff --git a/src/main/resources/images/card-diamond-9-3.png b/src/main/resources/images/card-diamond-9-3.png new file mode 100644 index 0000000..b2bfd22 Binary files /dev/null and b/src/main/resources/images/card-diamond-9-3.png differ diff --git a/src/main/resources/images/card-diamond-ace-3.png b/src/main/resources/images/card-diamond-ace-3.png new file mode 100644 index 0000000..29b3180 Binary files /dev/null and b/src/main/resources/images/card-diamond-ace-3.png differ diff --git a/src/main/resources/images/card-diamond-jack-3.png b/src/main/resources/images/card-diamond-jack-3.png new file mode 100644 index 0000000..1c7fab1 Binary files /dev/null and b/src/main/resources/images/card-diamond-jack-3.png differ diff --git a/src/main/resources/images/card-diamond-king-3.png b/src/main/resources/images/card-diamond-king-3.png new file mode 100644 index 0000000..aaa0ab5 Binary files /dev/null and b/src/main/resources/images/card-diamond-king-3.png differ diff --git a/src/main/resources/images/card-diamond-queen-3.png b/src/main/resources/images/card-diamond-queen-3.png new file mode 100644 index 0000000..04873ee Binary files /dev/null and b/src/main/resources/images/card-diamond-queen-3.png differ diff --git a/src/main/resources/images/card-heart-1-1.png b/src/main/resources/images/card-heart-1-1.png new file mode 100644 index 0000000..6418ba0 Binary files /dev/null and b/src/main/resources/images/card-heart-1-1.png differ diff --git a/src/main/resources/images/card-heart-1-3.png b/src/main/resources/images/card-heart-1-3.png new file mode 100644 index 0000000..44cc678 Binary files /dev/null and b/src/main/resources/images/card-heart-1-3.png differ diff --git a/src/main/resources/images/card-heart-10-3.png b/src/main/resources/images/card-heart-10-3.png new file mode 100644 index 0000000..a56f125 Binary files /dev/null and b/src/main/resources/images/card-heart-10-3.png differ diff --git a/src/main/resources/images/card-heart-2-3.png b/src/main/resources/images/card-heart-2-3.png new file mode 100644 index 0000000..5998f0b Binary files /dev/null and b/src/main/resources/images/card-heart-2-3.png differ diff --git a/src/main/resources/images/card-heart-3-3.png b/src/main/resources/images/card-heart-3-3.png new file mode 100644 index 0000000..de6a64e Binary files /dev/null and b/src/main/resources/images/card-heart-3-3.png differ diff --git a/src/main/resources/images/card-heart-4-3.png b/src/main/resources/images/card-heart-4-3.png new file mode 100644 index 0000000..1461c1f Binary files /dev/null and b/src/main/resources/images/card-heart-4-3.png differ diff --git a/src/main/resources/images/card-heart-5-3.png b/src/main/resources/images/card-heart-5-3.png new file mode 100644 index 0000000..9b25594 Binary files /dev/null and b/src/main/resources/images/card-heart-5-3.png differ diff --git a/src/main/resources/images/card-heart-6-3.png b/src/main/resources/images/card-heart-6-3.png new file mode 100644 index 0000000..a60ce4b Binary files /dev/null and b/src/main/resources/images/card-heart-6-3.png differ diff --git a/src/main/resources/images/card-heart-7-3.png b/src/main/resources/images/card-heart-7-3.png new file mode 100644 index 0000000..7d51eca Binary files /dev/null and b/src/main/resources/images/card-heart-7-3.png differ diff --git a/src/main/resources/images/card-heart-8-3.png b/src/main/resources/images/card-heart-8-3.png new file mode 100644 index 0000000..640bb09 Binary files /dev/null and b/src/main/resources/images/card-heart-8-3.png differ diff --git a/src/main/resources/images/card-heart-9-3.png b/src/main/resources/images/card-heart-9-3.png new file mode 100644 index 0000000..e13d594 Binary files /dev/null and b/src/main/resources/images/card-heart-9-3.png differ diff --git a/src/main/resources/images/card-heart-ace-3.png b/src/main/resources/images/card-heart-ace-3.png new file mode 100644 index 0000000..6b8e378 Binary files /dev/null and b/src/main/resources/images/card-heart-ace-3.png differ diff --git a/src/main/resources/images/card-heart-jack-3.png b/src/main/resources/images/card-heart-jack-3.png new file mode 100644 index 0000000..887ba39 Binary files /dev/null and b/src/main/resources/images/card-heart-jack-3.png differ diff --git a/src/main/resources/images/card-heart-king-3.png b/src/main/resources/images/card-heart-king-3.png new file mode 100644 index 0000000..947e312 Binary files /dev/null and b/src/main/resources/images/card-heart-king-3.png differ diff --git a/src/main/resources/images/card-heart-queen-3.png b/src/main/resources/images/card-heart-queen-3.png new file mode 100644 index 0000000..13a62a1 Binary files /dev/null and b/src/main/resources/images/card-heart-queen-3.png differ diff --git a/src/main/resources/images/card-pik-1-1.png b/src/main/resources/images/card-pik-1-1.png new file mode 100644 index 0000000..0e6cbde Binary files /dev/null and b/src/main/resources/images/card-pik-1-1.png differ diff --git a/src/main/resources/images/card-pik-1-3.png b/src/main/resources/images/card-pik-1-3.png new file mode 100644 index 0000000..b1e1080 Binary files /dev/null and b/src/main/resources/images/card-pik-1-3.png differ diff --git a/src/main/resources/images/card-pik-10-3.png b/src/main/resources/images/card-pik-10-3.png new file mode 100644 index 0000000..b40b0ce Binary files /dev/null and b/src/main/resources/images/card-pik-10-3.png differ diff --git a/src/main/resources/images/card-pik-2-3.png b/src/main/resources/images/card-pik-2-3.png new file mode 100644 index 0000000..44e3123 Binary files /dev/null and b/src/main/resources/images/card-pik-2-3.png differ diff --git a/src/main/resources/images/card-pik-3-3.png b/src/main/resources/images/card-pik-3-3.png new file mode 100644 index 0000000..a26b334 Binary files /dev/null and b/src/main/resources/images/card-pik-3-3.png differ diff --git a/src/main/resources/images/card-pik-4-3.png b/src/main/resources/images/card-pik-4-3.png new file mode 100644 index 0000000..efed0ae Binary files /dev/null and b/src/main/resources/images/card-pik-4-3.png differ diff --git a/src/main/resources/images/card-pik-5-3.png b/src/main/resources/images/card-pik-5-3.png new file mode 100644 index 0000000..97b766d Binary files /dev/null and b/src/main/resources/images/card-pik-5-3.png differ diff --git a/src/main/resources/images/card-pik-6-3.png b/src/main/resources/images/card-pik-6-3.png new file mode 100644 index 0000000..6a4e211 Binary files /dev/null and b/src/main/resources/images/card-pik-6-3.png differ diff --git a/src/main/resources/images/card-pik-7-3.png b/src/main/resources/images/card-pik-7-3.png new file mode 100644 index 0000000..7012f03 Binary files /dev/null and b/src/main/resources/images/card-pik-7-3.png differ diff --git a/src/main/resources/images/card-pik-8-3.png b/src/main/resources/images/card-pik-8-3.png new file mode 100644 index 0000000..fed7ff4 Binary files /dev/null and b/src/main/resources/images/card-pik-8-3.png differ diff --git a/src/main/resources/images/card-pik-9-3.png b/src/main/resources/images/card-pik-9-3.png new file mode 100644 index 0000000..924911d Binary files /dev/null and b/src/main/resources/images/card-pik-9-3.png differ diff --git a/src/main/resources/images/card-pik-ace-3.png b/src/main/resources/images/card-pik-ace-3.png new file mode 100644 index 0000000..37df3d3 Binary files /dev/null and b/src/main/resources/images/card-pik-ace-3.png differ diff --git a/src/main/resources/images/card-pik-jack-3.png b/src/main/resources/images/card-pik-jack-3.png new file mode 100644 index 0000000..f450d6b Binary files /dev/null and b/src/main/resources/images/card-pik-jack-3.png differ diff --git a/src/main/resources/images/card-pik-king-3.png b/src/main/resources/images/card-pik-king-3.png new file mode 100644 index 0000000..02ebde9 Binary files /dev/null and b/src/main/resources/images/card-pik-king-3.png differ diff --git a/src/main/resources/images/card-pik-queen-3.png b/src/main/resources/images/card-pik-queen-3.png new file mode 100644 index 0000000..0a5bc5a Binary files /dev/null and b/src/main/resources/images/card-pik-queen-3.png differ diff --git a/src/main/resources/images/chip-1-5.png b/src/main/resources/images/chip-1-5.png new file mode 100644 index 0000000..32474d6 Binary files /dev/null and b/src/main/resources/images/chip-1-5.png differ diff --git a/src/main/resources/images/chip-10-5.png b/src/main/resources/images/chip-10-5.png new file mode 100644 index 0000000..5cde84b Binary files /dev/null and b/src/main/resources/images/chip-10-5.png differ diff --git a/src/main/resources/images/chip-100-5.png b/src/main/resources/images/chip-100-5.png new file mode 100644 index 0000000..d039c12 Binary files /dev/null and b/src/main/resources/images/chip-100-5.png differ diff --git a/src/main/resources/images/chip-1000-5.png b/src/main/resources/images/chip-1000-5.png new file mode 100644 index 0000000..7178e4e Binary files /dev/null and b/src/main/resources/images/chip-1000-5.png differ diff --git a/src/main/resources/images/chip-10000-5.png b/src/main/resources/images/chip-10000-5.png new file mode 100644 index 0000000..7cbfb3c Binary files /dev/null and b/src/main/resources/images/chip-10000-5.png differ diff --git a/src/main/resources/images/chip-100000-5.png b/src/main/resources/images/chip-100000-5.png new file mode 100644 index 0000000..2eab023 Binary files /dev/null and b/src/main/resources/images/chip-100000-5.png differ diff --git a/src/main/resources/images/chip-2-5.png b/src/main/resources/images/chip-2-5.png new file mode 100644 index 0000000..2dd7a9a Binary files /dev/null and b/src/main/resources/images/chip-2-5.png differ diff --git a/src/main/resources/images/chip-20-5.png b/src/main/resources/images/chip-20-5.png new file mode 100644 index 0000000..a105f08 Binary files /dev/null and b/src/main/resources/images/chip-20-5.png differ diff --git a/src/main/resources/images/chip-200-5.png b/src/main/resources/images/chip-200-5.png new file mode 100644 index 0000000..1893e92 Binary files /dev/null and b/src/main/resources/images/chip-200-5.png differ diff --git a/src/main/resources/images/chip-2000-5.png b/src/main/resources/images/chip-2000-5.png new file mode 100644 index 0000000..e3e0ba0 Binary files /dev/null and b/src/main/resources/images/chip-2000-5.png differ diff --git a/src/main/resources/images/chip-20000-5.png b/src/main/resources/images/chip-20000-5.png new file mode 100644 index 0000000..9c6cb9d Binary files /dev/null and b/src/main/resources/images/chip-20000-5.png differ diff --git a/src/main/resources/images/chip-3-5.png b/src/main/resources/images/chip-3-5.png new file mode 100644 index 0000000..720c2a9 Binary files /dev/null and b/src/main/resources/images/chip-3-5.png differ diff --git a/src/main/resources/images/chip-4-5.png b/src/main/resources/images/chip-4-5.png new file mode 100644 index 0000000..61278fc Binary files /dev/null and b/src/main/resources/images/chip-4-5.png differ diff --git a/src/main/resources/images/chip-5-5.png b/src/main/resources/images/chip-5-5.png new file mode 100644 index 0000000..7c60b57 Binary files /dev/null and b/src/main/resources/images/chip-5-5.png differ diff --git a/src/main/resources/images/chip-50-5.png b/src/main/resources/images/chip-50-5.png new file mode 100644 index 0000000..4b2fda4 Binary files /dev/null and b/src/main/resources/images/chip-50-5.png differ diff --git a/src/main/resources/images/chip-500-5.png b/src/main/resources/images/chip-500-5.png new file mode 100644 index 0000000..49bc939 Binary files /dev/null and b/src/main/resources/images/chip-500-5.png differ diff --git a/src/main/resources/images/chip-5000-5.png b/src/main/resources/images/chip-5000-5.png new file mode 100644 index 0000000..927deb0 Binary files /dev/null and b/src/main/resources/images/chip-5000-5.png differ diff --git a/src/main/resources/images/chip-50000-5.png b/src/main/resources/images/chip-50000-5.png new file mode 100644 index 0000000..b67bea8 Binary files /dev/null and b/src/main/resources/images/chip-50000-5.png differ diff --git a/src/main/resources/images/chip-6-5.png b/src/main/resources/images/chip-6-5.png new file mode 100644 index 0000000..7aecc36 Binary files /dev/null and b/src/main/resources/images/chip-6-5.png differ diff --git a/src/main/resources/images/chip-7-5.png b/src/main/resources/images/chip-7-5.png new file mode 100644 index 0000000..8199a10 Binary files /dev/null and b/src/main/resources/images/chip-7-5.png differ diff --git a/src/main/resources/images/chip-8-5.png b/src/main/resources/images/chip-8-5.png new file mode 100644 index 0000000..a72e08c Binary files /dev/null and b/src/main/resources/images/chip-8-5.png differ diff --git a/src/main/resources/images/green-background-1.png b/src/main/resources/images/green-background-1.png new file mode 100644 index 0000000..20496df Binary files /dev/null and b/src/main/resources/images/green-background-1.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_1_created.png b/src/main/resources/images/lobbypictures/lobby_1_created.png new file mode 100644 index 0000000..cf9b467 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_1_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_1_running.png b/src/main/resources/images/lobbypictures/lobby_1_running.png new file mode 100644 index 0000000..fc44fad Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_1_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_2_created.png b/src/main/resources/images/lobbypictures/lobby_2_created.png new file mode 100644 index 0000000..5082013 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_2_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_2_running.png b/src/main/resources/images/lobbypictures/lobby_2_running.png new file mode 100644 index 0000000..9ed62c2 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_2_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_3_created.png b/src/main/resources/images/lobbypictures/lobby_3_created.png new file mode 100644 index 0000000..dda0d19 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_3_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_3_running.png b/src/main/resources/images/lobbypictures/lobby_3_running.png new file mode 100644 index 0000000..73450fb Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_3_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_4_created.png b/src/main/resources/images/lobbypictures/lobby_4_created.png new file mode 100644 index 0000000..34dc9cc Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_4_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_4_running.png b/src/main/resources/images/lobbypictures/lobby_4_running.png new file mode 100644 index 0000000..3ec3d4e Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_4_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_5_created.png b/src/main/resources/images/lobbypictures/lobby_5_created.png new file mode 100644 index 0000000..dacdba0 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_5_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_5_running.png b/src/main/resources/images/lobbypictures/lobby_5_running.png new file mode 100644 index 0000000..661615a Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_5_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_6_created.png b/src/main/resources/images/lobbypictures/lobby_6_created.png new file mode 100644 index 0000000..41ce0db Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_6_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_6_running.png b/src/main/resources/images/lobbypictures/lobby_6_running.png new file mode 100644 index 0000000..23fec97 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_6_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_7_created.png b/src/main/resources/images/lobbypictures/lobby_7_created.png new file mode 100644 index 0000000..710ac33 Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_7_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_7_running.png b/src/main/resources/images/lobbypictures/lobby_7_running.png new file mode 100644 index 0000000..b5e272f Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_7_running.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_8_created.png b/src/main/resources/images/lobbypictures/lobby_8_created.png new file mode 100644 index 0000000..60099ae Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_8_created.png differ diff --git a/src/main/resources/images/lobbypictures/lobby_8_running.png b/src/main/resources/images/lobbypictures/lobby_8_running.png new file mode 100644 index 0000000..6025a4d Binary files /dev/null and b/src/main/resources/images/lobbypictures/lobby_8_running.png differ diff --git a/src/main/resources/images/logo-black-1.png b/src/main/resources/images/logo-black-1.png new file mode 100644 index 0000000..73053b6 Binary files /dev/null and b/src/main/resources/images/logo-black-1.png differ diff --git a/src/main/resources/images/logo-white-1.png b/src/main/resources/images/logo-white-1.png new file mode 100644 index 0000000..e906bd4 Binary files /dev/null and b/src/main/resources/images/logo-white-1.png differ diff --git a/src/main/resources/images/logo-with-text-3.png b/src/main/resources/images/logo-with-text-3.png new file mode 100644 index 0000000..3a4fed7 Binary files /dev/null and b/src/main/resources/images/logo-with-text-3.png differ diff --git a/src/main/resources/images/old_images/chip-1-4.png b/src/main/resources/images/old_images/chip-1-4.png new file mode 100644 index 0000000..089c5b5 Binary files /dev/null and b/src/main/resources/images/old_images/chip-1-4.png differ diff --git a/src/main/resources/images/old_images/chip-10-4.png b/src/main/resources/images/old_images/chip-10-4.png new file mode 100644 index 0000000..a10e6bf Binary files /dev/null and b/src/main/resources/images/old_images/chip-10-4.png differ diff --git a/src/main/resources/images/old_images/chip-100-4.png b/src/main/resources/images/old_images/chip-100-4.png new file mode 100644 index 0000000..4ddbe1d Binary files /dev/null and b/src/main/resources/images/old_images/chip-100-4.png differ diff --git a/src/main/resources/images/old_images/chip-1000-4.png b/src/main/resources/images/old_images/chip-1000-4.png new file mode 100644 index 0000000..23b05d1 Binary files /dev/null and b/src/main/resources/images/old_images/chip-1000-4.png differ diff --git a/src/main/resources/images/old_images/chip-10000-4.png b/src/main/resources/images/old_images/chip-10000-4.png new file mode 100644 index 0000000..3fe9429 Binary files /dev/null and b/src/main/resources/images/old_images/chip-10000-4.png differ diff --git a/src/main/resources/images/old_images/chip-100000-4.png b/src/main/resources/images/old_images/chip-100000-4.png new file mode 100644 index 0000000..afe4afc Binary files /dev/null and b/src/main/resources/images/old_images/chip-100000-4.png differ diff --git a/src/main/resources/images/old_images/chip-2-4.png b/src/main/resources/images/old_images/chip-2-4.png new file mode 100644 index 0000000..c4bcb88 Binary files /dev/null and b/src/main/resources/images/old_images/chip-2-4.png differ diff --git a/src/main/resources/images/old_images/chip-20-4.png b/src/main/resources/images/old_images/chip-20-4.png new file mode 100644 index 0000000..49c9a9a Binary files /dev/null and b/src/main/resources/images/old_images/chip-20-4.png differ diff --git a/src/main/resources/images/old_images/chip-200-4.png b/src/main/resources/images/old_images/chip-200-4.png new file mode 100644 index 0000000..2ed608a Binary files /dev/null and b/src/main/resources/images/old_images/chip-200-4.png differ diff --git a/src/main/resources/images/old_images/chip-2000-4.png b/src/main/resources/images/old_images/chip-2000-4.png new file mode 100644 index 0000000..d73d9d8 Binary files /dev/null and b/src/main/resources/images/old_images/chip-2000-4.png differ diff --git a/src/main/resources/images/old_images/chip-20000-4.png b/src/main/resources/images/old_images/chip-20000-4.png new file mode 100644 index 0000000..d25c548 Binary files /dev/null and b/src/main/resources/images/old_images/chip-20000-4.png differ diff --git a/src/main/resources/images/old_images/chip-3-4.png b/src/main/resources/images/old_images/chip-3-4.png new file mode 100644 index 0000000..2a9c147 Binary files /dev/null and b/src/main/resources/images/old_images/chip-3-4.png differ diff --git a/src/main/resources/images/old_images/chip-4-4.png b/src/main/resources/images/old_images/chip-4-4.png new file mode 100644 index 0000000..e3cd0ae Binary files /dev/null and b/src/main/resources/images/old_images/chip-4-4.png differ diff --git a/src/main/resources/images/old_images/chip-5-4.png b/src/main/resources/images/old_images/chip-5-4.png new file mode 100644 index 0000000..89b8334 Binary files /dev/null and b/src/main/resources/images/old_images/chip-5-4.png differ diff --git a/src/main/resources/images/old_images/chip-50-4.png b/src/main/resources/images/old_images/chip-50-4.png new file mode 100644 index 0000000..e69468b Binary files /dev/null and b/src/main/resources/images/old_images/chip-50-4.png differ diff --git a/src/main/resources/images/old_images/chip-500-4.png b/src/main/resources/images/old_images/chip-500-4.png new file mode 100644 index 0000000..76ae2c2 Binary files /dev/null and b/src/main/resources/images/old_images/chip-500-4.png differ diff --git a/src/main/resources/images/old_images/chip-5000-4.png b/src/main/resources/images/old_images/chip-5000-4.png new file mode 100644 index 0000000..1708082 Binary files /dev/null and b/src/main/resources/images/old_images/chip-5000-4.png differ diff --git a/src/main/resources/images/old_images/chip-50000-4.png b/src/main/resources/images/old_images/chip-50000-4.png new file mode 100644 index 0000000..8030aee Binary files /dev/null and b/src/main/resources/images/old_images/chip-50000-4.png differ diff --git a/src/main/resources/images/old_images/chip-6-4.png b/src/main/resources/images/old_images/chip-6-4.png new file mode 100644 index 0000000..e7fea89 Binary files /dev/null and b/src/main/resources/images/old_images/chip-6-4.png differ diff --git a/src/main/resources/images/old_images/chip-7-4.png b/src/main/resources/images/old_images/chip-7-4.png new file mode 100644 index 0000000..62b452a Binary files /dev/null and b/src/main/resources/images/old_images/chip-7-4.png differ diff --git a/src/main/resources/images/old_images/chip-8-4.png b/src/main/resources/images/old_images/chip-8-4.png new file mode 100644 index 0000000..42119b2 Binary files /dev/null and b/src/main/resources/images/old_images/chip-8-4.png differ diff --git a/src/main/resources/images/placeholder-animation.mp4 b/src/main/resources/images/placeholder-animation.mp4 new file mode 100644 index 0000000..dc81e12 Binary files /dev/null and b/src/main/resources/images/placeholder-animation.mp4 differ diff --git a/src/main/resources/images/red-background-1.png b/src/main/resources/images/red-background-1.png new file mode 100644 index 0000000..fe375e9 Binary files /dev/null and b/src/main/resources/images/red-background-1.png differ diff --git a/src/main/resources/images/taskbar-logo-black-1.png b/src/main/resources/images/taskbar-logo-black-1.png new file mode 100644 index 0000000..f800ab8 Binary files /dev/null and b/src/main/resources/images/taskbar-logo-black-1.png differ diff --git a/src/main/resources/ui-structure/casinogameui.css b/src/main/resources/ui-structure/Casinogameui.css similarity index 100% rename from src/main/resources/ui-structure/casinogameui.css rename to src/main/resources/ui-structure/Casinogameui.css diff --git a/src/main/resources/ui-structure/Casinogameui.fxml b/src/main/resources/ui-structure/Casinogameui.fxml index 91bbe26..08bbf56 100644 --- a/src/main/resources/ui-structure/Casinogameui.fxml +++ b/src/main/resources/ui-structure/Casinogameui.fxml @@ -5,12 +5,12 @@ - + + stylesheets="@Casinogameui.css"> @@ -78,14 +78,14 @@ - + - + @@ -99,7 +99,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -129,7 +129,7 @@ - + diff --git a/src/main/resources/ui-structure/Casinomainui.fxml b/src/main/resources/ui-structure/Casinomainui.fxml index 2d18a48..7cae21e 100644 --- a/src/main/resources/ui-structure/Casinomainui.fxml +++ b/src/main/resources/ui-structure/Casinomainui.fxml @@ -106,20 +106,7 @@ - - - - - - - + diff --git a/src/main/resources/ui-structure/components/chatbox.fxml b/src/main/resources/ui-structure/components/Chatbox.fxml similarity index 98% rename from src/main/resources/ui-structure/components/chatbox.fxml rename to src/main/resources/ui-structure/components/Chatbox.fxml index 3e518ba..c61e28e 100644 --- a/src/main/resources/ui-structure/components/chatbox.fxml +++ b/src/main/resources/ui-structure/components/Chatbox.fxml @@ -21,7 +21,7 @@ xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui.ChatController" alignment="CENTER" - stylesheets="@chatui.css"> + stylesheets="@Chatui.css"> diff --git a/src/main/resources/ui-structure/components/chatui.css b/src/main/resources/ui-structure/components/Chatui.css similarity index 100% rename from src/main/resources/ui-structure/components/chatui.css rename to src/main/resources/ui-structure/components/Chatui.css diff --git a/src/main/resources/ui-structure/gameuicomponents/playerstatus.fxml b/src/main/resources/ui-structure/gameuicomponents/Playerstatus.fxml similarity index 100% rename from src/main/resources/ui-structure/gameuicomponents/playerstatus.fxml rename to src/main/resources/ui-structure/gameuicomponents/Playerstatus.fxml diff --git a/src/main/resources/ui-structure/gameuicomponents/taskbar.fxml b/src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml similarity index 100% rename from src/main/resources/ui-structure/gameuicomponents/taskbar.fxml rename to src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManagerTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManagerTest.java index 67aaae4..765deb0 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManagerTest.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManagerTest.java @@ -13,7 +13,7 @@ class LobbyButtonGridManagerTest { @BeforeEach void setUp() { gridPane = new GridPane(); - translationManager = new LobbyButtonTranslationManager(); + translationManager = LobbyButtonTranslationManager.getInstance(); translationManager.getButtonIdToLobbyId().clear(); gridManager = new LobbyButtonGridManager(gridPane, translationManager); } @@ -23,10 +23,4 @@ class LobbyButtonGridManagerTest { int lobbyId = gridManager.createLobby(); assertTrue(lobbyId > 0); } - - @Test - void testJoinLobbyPlaceholder() { - // check if exception is thrown - assertDoesNotThrow(() -> gridManager.joinLobby(123)); - } } diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManagerTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManagerTest.java index d19532a..dfcce6a 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManagerTest.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonTranslationManagerTest.java @@ -6,11 +6,12 @@ import java.util.Map; import org.junit.jupiter.api.*; class LobbyButtonTranslationManagerTest { + LobbyButtonTranslationManager manager; @BeforeEach void setUp() { - manager = new LobbyButtonTranslationManager(); + manager = LobbyButtonTranslationManager.getInstance(); manager.getButtonIdToLobbyId().clear(); }