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.
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/beendensind Aliase, die inAppaufMenuCommandregistriert werden (sieheApp.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.exitsistEnumMap<Direction, Room>(typisicher, schnell) stattMap<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.