Files
Jander_Semester2/Semesterprojekt/docs/architecture.md
Jean-Luc Makiola c205d1f49b docs: correct stale documentation and drop planning artifacts
The docs had drifted from the code and described things that never existed.

- architecture.md documented a QuitCommand class (quit/exit are aliases on
  MenuCommand) and a GameIO interface of read()/write(String); the real one is
  readLine() plus print(StyledText) with presentation hooks. Both corrected,
  along with the package diagram and the game-loop sketch.
- conventions.md listed a package tree missing save, menu, io.text and
  command.impl. Updated, including why MapLayout lives in game.
- data-structures.md claimed LinkedList was deliberately avoided (Pathfinder
  uses one for O(1) addFirst during path reconstruction), named the wrong queue
  for the Swing bridge, and listed an input history that was never built.
- Removed enhancement-ideas.md and implementation-status.md: a backlog and a
  phase tracker, neither of which documents the delivered project.

Also stop tracking the built jars. They still ship in the submission, but a
22 MB binary that changes on every build does not belong in git history.
2026-07-14 12:40:09 +02:00

113 lines
4.4 KiB
Markdown

# Architektur
## Schichten
```mermaid
flowchart TD
IO["io (Konsole / GUI)<br/>Interaktion mit Spieler"]
GAME["game (Engine/Loop)<br/>Spielfluss, hält World + Player"]
CMD["command (Commands)<br/>go, take, use, talk, ..."]
MODEL["model (Domain)<br/>Room, Item, Player, Npc, World"]
LOADER["loader (YAML + DTO)<br/>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<br/>AppGui.java"]
root --> model
root --> loader
root --> command
root --> game
root --> io
model --> model_files["World, Room,<br/>Player, Npc, Direction"]
model --> item
item --> item_files["Item (abstract)<br/>ReadableItem<br/>SwitchableItem<br/>PlainItem"]
loader --> loader_files["WorldLoader<br/>ReferenceResolver<br/>WorldValidator<br/>*Factory"]
loader --> dto
dto --> dto_files["RoomDto, ItemDto,<br/>NpcDto, GameDto,<br/>QuestDto, EndingDto, …"]
command --> cmd_core["Command (Interface)<br/>CommandRegistry<br/>CommandParser"]
command --> impl
impl --> impl_files["GoCommand, TakeCommand,<br/>DropCommand, UseCommand,<br/>ReadCommand, ExamineCommand,<br/>InventoryCommand, LookCommand,<br/>TalkCommand, GiveCommand,<br/>MapCommand, QuestsCommand,<br/>SaveCommand, MenuCommand,<br/>HelpCommand"]
game --> game_files["Game (Loop)<br/>GameContext/Session/State<br/>QuestEngine, EndingEngine,<br/>EscortEngine, Light,<br/>Pathfinder, MapLayout"]
io --> io_files["GameIO (Interface)<br/>ConsoleIO<br/>SwingIO<br/>io.text (Styled Output)"]
```
> **Hinweis:** Es gibt **keine** `QuitCommand`-Klasse. `quit`/`exit`/`beenden` sind Aliase,
> die in `App` auf `MenuCommand` registriert werden (siehe `App.play`).
## 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<Direction, Room>` (typisicher, schnell) statt `Map<String, String>` (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 (running) {
String input = io.readLine();
if (input == null) break; // EOF
ParsedCommand parsed = parser.parse(input);
registry.find(parsed.verb()).ifPresentOrElse(
cmd -> cmd.execute(ctx, parsed.args()),
() -> io.write("I don't understand '" + parsed.verb() + "'."));
publishHud(); // Engines ticken, HUD/Map/Quests updaten
maybeEnd(); // Ending-Bedingungen prüfen
}
```
## IO-Abstraktion
Konsole und GUI teilen sich `GameIO`:
```java
public interface GameIO {
String readLine(); // blockierende Leseoperation
void print(StyledText text); // einzige Ausgabe-Primitive
// Convenience + Presentation-Hooks als default-Methoden:
default void write(String text) { } // Kurzform für ungestylten Text
default void showRoom(RoomView v) { }
default void setHud(Hud hud) { }
default void setMap(MapView map) { }
default void setQuests(QuestView quests) { }
default void setMusic(String track) { }
default int choose(String title, List<String> options) { }
default void shutdown() { }
}
```
Ausgabe läuft **immer** über `print(StyledText)` — eine Liste von `Span`s mit `Style`.
`ConsoleIO` rendert sie als ANSI-Farben (oder ASCII, je nach Settings), `SwingIO` als
`StyledDocument` im `JTextPane`. Die `default`-Methoden geben der GUI Extra-Hooks
(Karte, Quest-Panel, Musik), die die Konsole schlicht ignoriert oder als Text ausgibt.
Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer
`LinkedBlockingQueue<String>`, die vom JTextField-ActionListener gefüllt wird.