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

4.4 KiB

Architektur

Schichten

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

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)
  • Domain-Tests können Objekte direkt im Code bauen, ohne YAML-Fixtures

Game-Loop (vereinfacht)

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:

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 Spans 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.