# Architektur ## Schichten ```mermaid flowchart TD IO["io (Konsole / GUI)
Interaktion mit Spieler"] GAME["game (Engine/Loop)
Spielfluss, hält World + Player"] CMD["command (Commands)
go, take, use, talk, ..."] MODEL["model (Domain)
Room, Item, Player, Npc, World"] LOADER["loader (YAML + DTO)
liest Ressourcen, validiert"] IO -- "liest/schreibt via GameIO" --> GAME GAME -- "dispatcht via CommandRegistry" --> CMD CMD -- "mutiert" --> MODEL LOADER -- "baut auf aus DTOs" --> MODEL ``` ## Package-Struktur ```mermaid flowchart LR root["thb.jeanluc.adventure"] root --> app["App.java
AppGui.java"] root --> model root --> loader root --> command root --> game root --> io model --> model_files["World, Room,
Player, Npc, Direction"] model --> item item --> item_files["Item (abstract)
ReadableItem
SwitchableItem
PlainItem"] loader --> loader_files["WorldLoader
ReferenceResolver
WorldValidator"] loader --> dto dto --> dto_files["RoomDto, ItemDto,
NpcDto, GameDto"] command --> cmd_core["Command (Interface)
CommandRegistry
CommandParser"] command --> impl impl --> impl_files["GoCommand, TakeCommand,
DropCommand, UseCommand,
InventoryCommand, LookCommand,
TalkCommand, GiveCommand,
HelpCommand, QuitCommand"] game --> game_files["Game (Loop)
GameContext"] io --> io_files["GameIO (Interface)
ConsoleIO
SwingIO"] ``` ## DTO vs. Domain-Trennung **Kernprinzip:** YAML wird in Records deserialisiert (DTOs), erst danach werden String-IDs zu Objekt-Referenzen aufgelöst. | | DTO | Domain | |---|---|---| | Typ | `record` | Lombok-Klasse | | Mutabilität | immutable | mutable wo nötig | | Felder | nur Daten + String-IDs | Objekt-Referenzen, EnumMap, etc. | | Zweck | Jackson-Mapping | Spielablauf | | Tests | Loader-Tests | Domain-Tests, brauchen kein YAML | **Warum getrennt?** - Domain-Klassen müssen nicht mit Jackson-Annotations verschmutzt werden - `Room.exits` ist `EnumMap` (typisicher, schnell) statt `Map` (was zum YAML passt) - Validierung passiert beim Übergang DTO→Domain (siehe [loading-flow.md](loading-flow.md)) - Domain-Tests können Objekte direkt im Code bauen, ohne YAML-Fixtures ## Game-Loop (vereinfacht) ```java while (!game.isOver()) { String input = io.read(); ParsedCommand parsed = parser.parse(input); Command cmd = registry.get(parsed.verb()); if (cmd == null) { io.write("Unbekannter Befehl."); } else { cmd.execute(context, parsed.args()); } } ``` ## IO-Abstraktion Konsole und GUI teilen sich `GameIO`: ```java public interface GameIO { String read(); // blockierende Leseoperation void write(String text); } ``` Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer `BlockingQueue`, die vom JTextField-ActionListener gefüllt wird.