Weiter gebastelt

This commit is contained in:
2026-06-09 19:25:30 +02:00
parent 5e85051716
commit edb9cfc946
54 changed files with 1088 additions and 356 deletions

View File

@@ -53,7 +53,9 @@ public final class MapData {
public final byte[] splatA;
/** Texturpfade für Basis-Terrain (4 Slots, "" = Standard-Textur). */
public final String[] terrainTextures = new String[]{"", "", "", ""};
public final String[] terrainTextures = new String[]{"", "", "", ""};
/** Normal-Map-Pfade für Basis-Terrain (4 Slots, "" = keine Normal-Map). */
public final String[] terrainNormalMaps = new String[]{"", "", "", ""};
/** Splatmap Rot-Kanal Gebirge: Tex1-Helligkeit, immer 255 [SPLAT_SIZE²]. */
public final byte[] upperSplatR;
@@ -65,7 +67,9 @@ public final class MapData {
public final byte[] upperSplatA;
/** Texturpfade für Gebirge (4 Slots, "" = Standard-Textur). */
public final String[] upperTextures = new String[]{"", "", "", ""};
public final String[] upperTextures = new String[]{"", "", "", ""};
/** Normal-Map-Pfade für Gebirge (4 Slots, "" = keine Normal-Map). */
public final String[] upperNormalMaps = new String[]{"", "", "", ""};
/** Gras-Dichte [SPLAT_SIZE²], Bytes 0255 (0=kein Gras, 255=max Dichte). */
public final byte[] grassDensity;

View File

@@ -55,7 +55,7 @@ public final class MapIO {
}
private static final int MAGIC = 0x424C4947; // "BLIG"
private static final int VERSION = 10;
private static final int VERSION = 11;
// Größen älterer Saves (v≤9) für Migrations-Upsampling
private static final int OLD_TERRAIN_VERTS = 4097;
@@ -127,6 +127,9 @@ public final class MapIO {
out.writeInt(slotEnd);
for (int i = 0; i < slotEnd; i++) out.writeUTF(slots[i] != null ? slots[i] : "");
out.write(data.grassTextureMap);
// v11: normal-map-pfade
writeStrings(out, data.terrainNormalMaps);
writeStrings(out, data.upperNormalMaps);
}
// Atomares Umbenennen: erst wenn die Datei vollständig ist ersetzen wir die alte.
// ATOMIC_MOVE schlägt auf manchen Systemen cross-device fehl → Fallback auf REPLACE_EXISTING.
@@ -246,6 +249,10 @@ public final class MapIO {
upsampleBytes(old, OLD_SPLAT_SIZE, data.grassTextureMap, MapData.SPLAT_SIZE);
}
}
if (version >= 11) {
readStrings(in, data.terrainNormalMaps);
readStrings(in, data.upperNormalMaps);
}
}
return data;
}

View File

@@ -12,11 +12,12 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Lädt und speichert {@link Item}-Instanzen als JSON.
* Dateiformat: {@code <itemId>.item} im items/-Verzeichnis.
* Dateiformat: {@code items/<category>/<itemId>.item}.
* Liste wird nach Kategorie-Ordinal, dann nach Name (TextReference-ID) sortiert.
*/
public final class ItemIO {
@@ -36,9 +37,17 @@ public final class ItemIO {
public static void save(Item item, Path itemDir) throws IOException {
if (item.getItemId() == null || item.getItemId().isBlank())
throw new IllegalArgumentException("itemId darf nicht leer sein");
Files.createDirectories(itemDir);
Files.writeString(itemDir.resolve(item.getItemId() + EXTENSION),
GSON.toJson(item), StandardCharsets.UTF_8);
Path targetDir = categoryDir(item, itemDir);
Files.createDirectories(targetDir);
Path newPath = targetDir.resolve(item.getItemId() + EXTENSION);
Files.writeString(newPath, GSON.toJson(item), StandardCharsets.UTF_8);
// Entferne veraltete Kopien an anderen Orten (z.B. nach Kategorie-Wechsel)
try (Stream<Path> walk = Files.walk(itemDir)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.getFileName().toString().equals(item.getItemId() + EXTENSION))
.filter(p -> !p.equals(newPath))
.forEach(p -> { try { Files.deleteIfExists(p); } catch (IOException ignored) {} });
}
log.debug("[ItemIO] Gespeichert: {}", item.getItemId());
}
@@ -49,8 +58,9 @@ public final class ItemIO {
public static List<Item> loadAll(Path itemDir) {
List<Item> result = new ArrayList<>();
if (!Files.isDirectory(itemDir)) return result;
try (Stream<Path> walk = Files.list(itemDir)) {
walk.filter(p -> p.toString().endsWith(EXTENSION))
try (Stream<Path> walk = Files.walk(itemDir)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(EXTENSION))
.sorted()
.forEach(p -> {
try { result.add(load(p)); }
@@ -64,6 +74,19 @@ public final class ItemIO {
}
public static void delete(String itemId, Path itemDir) throws IOException {
Files.deleteIfExists(itemDir.resolve(itemId + EXTENSION));
if (!Files.isDirectory(itemDir)) return;
try (Stream<Path> walk = Files.walk(itemDir)) {
List<Path> found = walk.filter(Files::isRegularFile)
.filter(p -> p.getFileName().toString().equals(itemId + EXTENSION))
.collect(Collectors.toList());
for (Path p : found) Files.deleteIfExists(p);
}
}
// ── Helpers ───────────────────────────────────────────────────────────────
private static Path categoryDir(Item item, Path itemDir) {
if (item.getCategory() == null) return itemDir;
return itemDir.resolve(item.getCategory().name().toLowerCase());
}
}