semesterprojekt: implement full text adventure (phases 1-7)
Walking skeleton through Swing GUI: YAML-driven world (4 rooms, 4 items, 1 NPC), HashMap command dispatch with parser, three-tier item hierarchy (readable / switchable / plain), and end-to-end NPC give/receive flow. 67 tests green.
This commit is contained in:
93
Semesterprojekt/docs/architecture.md
Normal file
93
Semesterprojekt/docs/architecture.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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"]
|
||||
loader --> dto
|
||||
dto --> dto_files["RoomDto, ItemDto,<br/>NpcDto, GameDto"]
|
||||
|
||||
command --> cmd_core["Command (Interface)<br/>CommandRegistry<br/>CommandParser"]
|
||||
command --> impl
|
||||
impl --> impl_files["GoCommand, TakeCommand,<br/>DropCommand, UseCommand,<br/>InventoryCommand, LookCommand,<br/>TalkCommand, GiveCommand,<br/>HelpCommand, QuitCommand"]
|
||||
|
||||
game --> game_files["Game (Loop)<br/>GameContext"]
|
||||
|
||||
io --> io_files["GameIO (Interface)<br/>ConsoleIO<br/>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<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 (!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<String>`, die vom JTextField-ActionListener gefüllt wird.
|
||||
Reference in New Issue
Block a user