# Loading-Flow Wie aus 4 YAML-Dateien eine spielbare `World` mit aufgelösten Referenzen wird. ## Phasen ```mermaid flowchart TD P1["Phase 1: YAML → DTOs
Jackson liest items.yaml, npcs.yaml,
rooms.yaml, game.yaml in Records.
Nur Daten, IDs als Strings."] P2["Phase 2: Domain-Shells erzeugen
Pro DTO ein leeres Domain-Objekt.
itemRegistry, npcRegistry, roomRegistry."] P3["Phase 3: Referenzen auflösen
Room: items-ids → Item-Objekte
Room: npcs-ids → Npc-Objekte
Room: exits-Strings → Direction + Room-Ref
Npc: reactions auflösen (gives, consumes)"] P4["Phase 4: Validierung
Validierungsregeln aus yaml-schemas.md.
Wirft WorldLoadException bei Verstoss."] P5["Phase 5: Player + World zusammenstellen
Player mit startRoom + startGold.
World für den Game-Loop bereit."] P1 --> P2 --> P3 --> P4 --> P5 ``` ## Klassen-Aufteilung | Klasse | Verantwortung | |---|---| | `WorldLoader` | Orchestriert die Phasen, public API: `World load()` | | `ReferenceResolver` | Phase 3 isoliert (testbar) | | `WorldValidator` | Phase 4 isoliert (testbar) | | `WorldLoadException` | Eigene RuntimeException mit aussagekräftiger Message | ## Beispiel-Skizze ```java public class WorldLoader { private final ObjectMapper yaml = new ObjectMapper(new YAMLFactory()); public World load() { // Phase 1 List itemDtos = readList("/world/items.yaml", ItemDto.class); List npcDtos = readList("/world/npcs.yaml", NpcDto.class); List roomDtos = readList("/world/rooms.yaml", RoomDto.class); GameDto gameDto = readSingle("/world/game.yaml", GameDto.class); // Phase 2: Registries Map items = itemDtos.stream() .collect(Collectors.toMap(ItemDto::id, ItemFactory::fromDto)); Map npcs = npcDtos.stream() .collect(Collectors.toMap(NpcDto::id, NpcFactory::fromDto)); Map rooms = roomDtos.stream() .collect(Collectors.toMap(RoomDto::id, RoomFactory::shellFromDto)); // Phase 3: Referenzen auflösen ReferenceResolver resolver = new ReferenceResolver(items, npcs, rooms); resolver.resolveRooms(roomDtos); resolver.resolveNpcs(npcDtos); // Phase 4: validieren new WorldValidator(items, npcs, rooms, gameDto).validate(); // Phase 5 Player player = new Player(rooms.get(gameDto.startRoom()), gameDto.startGold()); return new World(rooms, items, npcs, player, gameDto); } } ``` ## Warum getrennte Phasen? - **Testbarkeit:** Resolver und Validator können isoliert mit fertigen DTOs gefüttert werden, ohne YAML auf der Platte - **Fehlerlokalisierung:** „Resolver hat versagt" vs. „Validator hat versagt" sind unterschiedliche Diagnosen - **Zirkuläre Referenzen** (Raum A → Raum B → Raum A) werden problemlos: in Phase 2 sind beide Shells da, Phase 3 verlinkt - **Fail-fast:** Spiel startet nicht mit kaputter Welt — besser als NullPointer beim ersten `go north` ## Tests pro Phase | Test | Inhalt | |---|---| | `WorldLoaderTest` | Happy-Path: lädt Test-Fixtures aus `src/test/resources/world/` | | `ReferenceResolverTest` | DTOs als Eingabe, prüft Auflösung | | `WorldValidatorTest` | Pro Validierungsregel ein Test, der den Fehler triggert | | `WorldValidatorTest.happy()` | Vollständig valide Welt darf nicht werfen |