Weiter gebastelt
This commit is contained in:
@@ -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 0–255 (0=kein Gras, 255=max Dichte). */
|
||||
public final byte[] grassDensity;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user