diff --git a/chunky/src/java/se/llbit/chunky/entity/PaintingEntity.java b/chunky/src/java/se/llbit/chunky/entity/PaintingEntity.java index 43b9192fe..fb86e1571 100644 --- a/chunky/src/java/se/llbit/chunky/entity/PaintingEntity.java +++ b/chunky/src/java/se/llbit/chunky/entity/PaintingEntity.java @@ -33,7 +33,7 @@ public class PaintingEntity extends Entity { - static class Painting { + public static class Painting { protected final Quad[] quads; protected final Material material; @@ -78,58 +78,7 @@ public Painting(Texture painting, int w, int h) { static final Map paintings = new HashMap<>(); static { - paintings.put("Kebab", new Painting(Texture.paintingKebab, 1, 1)); - paintings.put("minecraft:kebab", new Painting(Texture.paintingKebab, 1, 1)); - paintings.put("Aztec", new Painting(Texture.paintingAztec, 1, 1)); - paintings.put("minecraft:aztec", new Painting(Texture.paintingAztec, 1, 1)); - paintings.put("Alban", new Painting(Texture.paintingAlban, 1, 1)); - paintings.put("minecraft:alban", new Painting(Texture.paintingAlban, 1, 1)); - paintings.put("Aztec2", new Painting(Texture.paintingAztec2, 1, 1)); - paintings.put("minecraft:aztec2", new Painting(Texture.paintingAztec2, 1, 1)); - paintings.put("Bomb", new Painting(Texture.paintingBomb, 1, 1)); - paintings.put("minecraft:bomb", new Painting(Texture.paintingBomb, 1, 1)); - paintings.put("Plant", new Painting(Texture.paintingPlant, 1, 1)); - paintings.put("minecraft:plant", new Painting(Texture.paintingPlant, 1, 1)); - paintings.put("Wasteland", new Painting(Texture.paintingWasteland, 1, 1)); - paintings.put("minecraft:wasteland", new Painting(Texture.paintingWasteland, 1, 1)); - paintings.put("Wanderer", new Painting(Texture.paintingWanderer, 1, 2)); - paintings.put("minecraft:wanderer", new Painting(Texture.paintingWanderer, 1, 2)); - paintings.put("Graham", new Painting(Texture.paintingGraham, 1, 2)); - paintings.put("minecraft:graham", new Painting(Texture.paintingGraham, 1, 2)); - paintings.put("Pool", new Painting(Texture.paintingPool, 2, 1)); - paintings.put("minecraft:pool", new Painting(Texture.paintingPool, 2, 1)); - paintings.put("Courbet", new Painting(Texture.paintingCourbet, 2, 1)); - paintings.put("minecraft:courbet", new Painting(Texture.paintingCourbet, 2, 1)); - paintings.put("Sunset", new Painting(Texture.paintingSunset, 2, 1)); - paintings.put("minecraft:sunset", new Painting(Texture.paintingSunset, 2, 1)); - paintings.put("Sea", new Painting(Texture.paintingSea, 2, 1)); - paintings.put("minecraft:sea", new Painting(Texture.paintingSea, 2, 1)); - paintings.put("Creebet", new Painting(Texture.paintingCreebet, 2, 1)); - paintings.put("minecraft:creebet", new Painting(Texture.paintingCreebet, 2, 1)); - paintings.put("Match", new Painting(Texture.paintingMatch, 2, 2)); - paintings.put("minecraft:match", new Painting(Texture.paintingMatch, 2, 2)); - paintings.put("Bust", new Painting(Texture.paintingBust, 2, 2)); - paintings.put("minecraft:bust", new Painting(Texture.paintingBust, 2, 2)); - paintings.put("Stage", new Painting(Texture.paintingStage, 2, 2)); - paintings.put("minecraft:stage", new Painting(Texture.paintingStage, 2, 2)); - paintings.put("Void", new Painting(Texture.paintingVoid, 2, 2)); - paintings.put("minecraft:void", new Painting(Texture.paintingVoid, 2, 2)); - paintings.put("SkullAndRoses", new Painting(Texture.paintingSkullAndRoses, 2, 2)); - paintings.put("minecraft:skull_and_roses", new Painting(Texture.paintingSkullAndRoses, 2, 2)); - paintings.put("Wither", new Painting(Texture.paintingWither, 2, 2)); - paintings.put("minecraft:wither", new Painting(Texture.paintingWither, 2, 2)); - paintings.put("Fighters", new Painting(Texture.paintingFighters, 4, 2)); - paintings.put("minecraft:fighters", new Painting(Texture.paintingFighters, 4, 2)); - paintings.put("Skeleton", new Painting(Texture.paintingSkeleton, 4, 3)); - paintings.put("minecraft:skeleton", new Painting(Texture.paintingSkeleton, 4, 3)); - paintings.put("DonkeyKong", new Painting(Texture.paintingDonkeyKong, 4, 3)); - paintings.put("minecraft:donkey_kong", new Painting(Texture.paintingDonkeyKong, 4, 3)); - paintings.put("Pointer", new Painting(Texture.paintingPointer, 4, 4)); - paintings.put("minecraft:pointer", new Painting(Texture.paintingPointer, 4, 4)); - paintings.put("Pigscene", new Painting(Texture.paintingPigscene, 4, 4)); - paintings.put("minecraft:pigscene", new Painting(Texture.paintingPigscene, 4, 4)); - paintings.put("BurningSkull", new Painting(Texture.paintingBurningSkull, 4, 4)); - paintings.put("minecraft:burning_skull", new Painting(Texture.paintingBurningSkull, 4, 4)); + resetPaintings(); } private static final Material BACK_MATERIAL = new TextureMaterial(Texture.paintingBack); @@ -204,4 +153,70 @@ public static Entity fromJson(JsonObject json) { double angle = json.get("angle").doubleValue(0.0); return new PaintingEntity(position, art, angle); } + + public static void resetPaintings() { + paintings.clear(); + + // hard-coded pre-24w18a paintings with legacy aliases + paintings.put("Kebab", new Painting(Texture.paintingKebab, 1, 1)); + paintings.put("minecraft:kebab", new Painting(Texture.paintingKebab, 1, 1)); + paintings.put("Aztec", new Painting(Texture.paintingAztec, 1, 1)); + paintings.put("minecraft:aztec", new Painting(Texture.paintingAztec, 1, 1)); + paintings.put("Alban", new Painting(Texture.paintingAlban, 1, 1)); + paintings.put("minecraft:alban", new Painting(Texture.paintingAlban, 1, 1)); + paintings.put("Aztec2", new Painting(Texture.paintingAztec2, 1, 1)); + paintings.put("minecraft:aztec2", new Painting(Texture.paintingAztec2, 1, 1)); + paintings.put("Bomb", new Painting(Texture.paintingBomb, 1, 1)); + paintings.put("minecraft:bomb", new Painting(Texture.paintingBomb, 1, 1)); + paintings.put("Plant", new Painting(Texture.paintingPlant, 1, 1)); + paintings.put("minecraft:plant", new Painting(Texture.paintingPlant, 1, 1)); + paintings.put("Wasteland", new Painting(Texture.paintingWasteland, 1, 1)); + paintings.put("minecraft:wasteland", new Painting(Texture.paintingWasteland, 1, 1)); + paintings.put("Wanderer", new Painting(Texture.paintingWanderer, 1, 2)); + paintings.put("minecraft:wanderer", new Painting(Texture.paintingWanderer, 1, 2)); + paintings.put("Graham", new Painting(Texture.paintingGraham, 1, 2)); + paintings.put("minecraft:graham", new Painting(Texture.paintingGraham, 1, 2)); + paintings.put("Pool", new Painting(Texture.paintingPool, 2, 1)); + paintings.put("minecraft:pool", new Painting(Texture.paintingPool, 2, 1)); + paintings.put("Courbet", new Painting(Texture.paintingCourbet, 2, 1)); + paintings.put("minecraft:courbet", new Painting(Texture.paintingCourbet, 2, 1)); + paintings.put("Sunset", new Painting(Texture.paintingSunset, 2, 1)); + paintings.put("minecraft:sunset", new Painting(Texture.paintingSunset, 2, 1)); + paintings.put("Sea", new Painting(Texture.paintingSea, 2, 1)); + paintings.put("minecraft:sea", new Painting(Texture.paintingSea, 2, 1)); + paintings.put("Creebet", new Painting(Texture.paintingCreebet, 2, 1)); + paintings.put("minecraft:creebet", new Painting(Texture.paintingCreebet, 2, 1)); + paintings.put("Match", new Painting(Texture.paintingMatch, 2, 2)); + paintings.put("minecraft:match", new Painting(Texture.paintingMatch, 2, 2)); + paintings.put("Bust", new Painting(Texture.paintingBust, 2, 2)); + paintings.put("minecraft:bust", new Painting(Texture.paintingBust, 2, 2)); + paintings.put("Stage", new Painting(Texture.paintingStage, 2, 2)); + paintings.put("minecraft:stage", new Painting(Texture.paintingStage, 2, 2)); + paintings.put("Void", new Painting(Texture.paintingVoid, 2, 2)); + paintings.put("minecraft:void", new Painting(Texture.paintingVoid, 2, 2)); + paintings.put("SkullAndRoses", new Painting(Texture.paintingSkullAndRoses, 2, 2)); + paintings.put("minecraft:skull_and_roses", new Painting(Texture.paintingSkullAndRoses, 2, 2)); + paintings.put("Wither", new Painting(Texture.paintingWither, 2, 2)); + paintings.put("minecraft:wither", new Painting(Texture.paintingWither, 2, 2)); + paintings.put("Fighters", new Painting(Texture.paintingFighters, 4, 2)); + paintings.put("minecraft:fighters", new Painting(Texture.paintingFighters, 4, 2)); + paintings.put("Skeleton", new Painting(Texture.paintingSkeleton, 4, 3)); + paintings.put("minecraft:skeleton", new Painting(Texture.paintingSkeleton, 4, 3)); + paintings.put("DonkeyKong", new Painting(Texture.paintingDonkeyKong, 4, 3)); + paintings.put("minecraft:donkey_kong", new Painting(Texture.paintingDonkeyKong, 4, 3)); + paintings.put("Pointer", new Painting(Texture.paintingPointer, 4, 4)); + paintings.put("minecraft:pointer", new Painting(Texture.paintingPointer, 4, 4)); + paintings.put("Pigscene", new Painting(Texture.paintingPigscene, 4, 4)); + paintings.put("minecraft:pigscene", new Painting(Texture.paintingPigscene, 4, 4)); + paintings.put("BurningSkull", new Painting(Texture.paintingBurningSkull, 4, 4)); + paintings.put("minecraft:burning_skull", new Painting(Texture.paintingBurningSkull, 4, 4)); + } + + public static void registerPainting(String id, Painting painting) { + paintings.put(id, painting); + } + + public static boolean containsPainting(String id) { + return paintings.containsKey(id); + } } diff --git a/chunky/src/java/se/llbit/chunky/resources/DataPackUtil.java b/chunky/src/java/se/llbit/chunky/resources/DataPackUtil.java new file mode 100644 index 000000000..e8803b6d9 --- /dev/null +++ b/chunky/src/java/se/llbit/chunky/resources/DataPackUtil.java @@ -0,0 +1,47 @@ +package se.llbit.chunky.resources; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class DataPackUtil { + private DataPackUtil() { + } + + public static void forEachDataRegistryEntry(LayeredResourcePacks resourcePacks, String registryPath, Consumer consumer) { + for (LayeredResourcePacks.Entry data : resourcePacks.getAllEntries("data")) { + try (Stream namespaces = Files.list(data.getPath())) { + namespaces.forEach(ns -> { + String namespace = String.valueOf(ns.getFileName()); + Path entriesPath = ns; + for (String part : registryPath.split("/")) { + entriesPath = entriesPath.resolve(part); + } + try (Stream entriesStream = Files.walk(entriesPath)) { + entriesStream + .filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)) + .forEach(file -> { + String name = file.getFileName().toString(); + if (name.toLowerCase().endsWith(".json")) { + name = name.substring(0, name.length() - ".json".length()); + } + consumer.accept(new DataRegistryEntry(namespace, name, file)); + }); + } catch (IOException ignored) { + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public record DataRegistryEntry(String namespace, String name, Path path) { + public String getNamespacedName() { + return namespace + ":" + name; + } + } +} diff --git a/chunky/src/java/se/llbit/chunky/resources/ResourcePackBiomeLoader.java b/chunky/src/java/se/llbit/chunky/resources/ResourcePackBiomeLoader.java index 83f06965d..73324a79b 100644 --- a/chunky/src/java/se/llbit/chunky/resources/ResourcePackBiomeLoader.java +++ b/chunky/src/java/se/llbit/chunky/resources/ResourcePackBiomeLoader.java @@ -27,11 +27,7 @@ import java.io.IOException; import java.io.Reader; import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.util.ArrayList; import java.util.Optional; -import java.util.stream.Stream; public class ResourcePackBiomeLoader implements ResourcePackLoader.PackLoader { public ResourcePackBiomeLoader() { @@ -57,69 +53,36 @@ protected static class BiomeEffects { @Override public boolean load(LayeredResourcePacks resourcePacks) { - for (LayeredResourcePacks.Entry data : resourcePacks.getAllEntries("data")) { - try (Stream namespaces = Files.list(data.getPath())) { - namespaces.forEach(ns -> { - String namespace = String.valueOf(ns.getFileName()); + DataPackUtil.forEachDataRegistryEntry(resourcePacks, "worldgen/biome", biome -> { + if (!Biomes.contains(biome.getNamespacedName())) { + try (Reader f = Files.newBufferedReader(biome.path())) { + BiomeJson json = GSON.fromJson(f, BiomeJson.class); - Path biomes = ns.resolve("worldgen").resolve("biome"); - try (Stream biomeStream = Files.walk(biomes)) { - biomeStream - .filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)) - .forEach(biome -> { - if (biome.toString().endsWith(".json")) { - String biomeName = getBiomeName(biomes.relativize(biome)); - String resourceLocation = namespace + ":" + biomeName; + BiomeBuilder builder = Biome.create(biome.getNamespacedName(), biome.name(), json.temperature, json.downfall); + Optional.ofNullable(json.effects.foliage_color).ifPresent(builder::foliageColor); + Optional.ofNullable(json.effects.grass_color).ifPresent(builder::grassColor); + Optional.ofNullable(json.effects.water_color).ifPresent(builder::waterColor); + Optional.ofNullable(json.effects.grass_color_modifier).ifPresent(modifier -> { + switch (modifier.toLowerCase()) { + case "none": + break; + case "dark_forest": + builder.darkForest(); + break; + case "swamp": + builder.swamp(); + break; + default: + Log.warnf("Unsupported biome `grass_modifier_color`: %s", modifier); + } + }); + // TODO Custom fog colors - if (!Biomes.contains(resourceLocation)) { - try (Reader f = Files.newBufferedReader(biome)) { - BiomeJson json = GSON.fromJson(f, BiomeJson.class); - - BiomeBuilder builder = Biome.create(resourceLocation, biomeName, json.temperature, json.downfall); - Optional.ofNullable(json.effects.foliage_color).ifPresent(builder::foliageColor); - Optional.ofNullable(json.effects.grass_color).ifPresent(builder::grassColor); - Optional.ofNullable(json.effects.water_color).ifPresent(builder::waterColor); - Optional.ofNullable(json.effects.grass_color_modifier).ifPresent(modifier -> { - switch (modifier.toLowerCase()) { - case "none": - break; - case "dark_forest": - builder.darkForest(); - break; - case "swamp": - builder.swamp(); - break; - default: - Log.warnf("Unsupported biome `grass_modifier_color`: %s", modifier); - } - }); - // TODO Custom fog colors - - Biomes.register(builder); - } catch (IOException ignored) { - } - } - } - }); - } catch (IOException ignored) { - } - }); - } catch (IOException e) { - throw new RuntimeException(e); + Biomes.register(builder); + } catch (IOException ignored) { + } } - } - + }); return false; } - - private static String getBiomeName(Path biome) { - ArrayList path = new ArrayList<>(); - biome.iterator().forEachRemaining(p -> path.add(String.valueOf(p))); - - String out = String.join("/", path); - if (out.toLowerCase().endsWith(".json")) { - out = out.substring(0, out.length() - ".json".length()); - } - return out; - } } diff --git a/chunky/src/java/se/llbit/chunky/resources/ResourcePackLoader.java b/chunky/src/java/se/llbit/chunky/resources/ResourcePackLoader.java index c5717a444..a7455a149 100644 --- a/chunky/src/java/se/llbit/chunky/resources/ResourcePackLoader.java +++ b/chunky/src/java/se/llbit/chunky/resources/ResourcePackLoader.java @@ -18,6 +18,7 @@ package se.llbit.chunky.resources; import se.llbit.chunky.PersistentSettings; +import se.llbit.chunky.entity.PaintingEntity; import se.llbit.chunky.world.biome.Biomes; import se.llbit.log.Log; @@ -38,6 +39,7 @@ public class ResourcePackLoader { static { ResourcePackLoader.PACK_LOADER_FACTORIES.add(() -> new ResourcePackTextureLoader(TexturePackLoader.ALL_TEXTURES)); ResourcePackLoader.PACK_LOADER_FACTORIES.add(ResourcePackBiomeLoader::new); + ResourcePackLoader.PACK_LOADER_FACTORIES.add(ResourcePackPaintingLoader::new); } public interface PackLoader { @@ -119,6 +121,7 @@ public static void loadAndPersistResourcePacks(List resourcePacks) { public static void loadResourcePacks(List resourcePacks) { TextureCache.reset(); Biomes.reset(); + PaintingEntity.resetPaintings(); if (ResourcePackLoader.resourcePacks != null) { try { diff --git a/chunky/src/java/se/llbit/chunky/resources/ResourcePackPaintingLoader.java b/chunky/src/java/se/llbit/chunky/resources/ResourcePackPaintingLoader.java new file mode 100644 index 000000000..8b6d9b649 --- /dev/null +++ b/chunky/src/java/se/llbit/chunky/resources/ResourcePackPaintingLoader.java @@ -0,0 +1,53 @@ +package se.llbit.chunky.resources; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import se.llbit.chunky.entity.PaintingEntity; +import se.llbit.chunky.resources.texturepack.SimpleTexture; +import se.llbit.log.Log; +import se.llbit.util.Pair; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; + +public class ResourcePackPaintingLoader implements ResourcePackLoader.PackLoader { + + protected static final Gson GSON = new GsonBuilder() + .disableJdkUnsafe() + .setLenient() + .create(); + + protected static class PaintingVariantJson { + public String asset_id; + public int width; + public int height; + + public Pair getAsset() { + String[] parts = asset_id.split(":"); + return new Pair<>(parts[0], parts[1]); + } + } + + @Override + public boolean load(LayeredResourcePacks resourcePacks) { + DataPackUtil.forEachDataRegistryEntry(resourcePacks, "painting_variant", paintingVariant -> { + if (!PaintingEntity.containsPainting(paintingVariant.getNamespacedName())) { + try (Reader f = Files.newBufferedReader(paintingVariant.path())) { + PaintingVariantJson json = GSON.fromJson(f, PaintingVariantJson.class); + Texture paintingTexture = new Texture(); + Pair asset = json.getAsset(); + if (!ResourcePackLoader.loadResources( + ResourcePackTextureLoader.singletonLoader(json.asset_id, new SimpleTexture("assets/" + asset.thing1 + "/textures/painting/" + asset.thing2, paintingTexture))) + ) { + Log.warnf("Failed to load painting texture: %s", json.asset_id); + } + PaintingEntity.registerPainting(paintingVariant.getNamespacedName(), new PaintingEntity.Painting(paintingTexture, json.width, json.height)); + } catch (IOException ignored) { + Log.warnf("Failed to load painting variant: %s", paintingVariant.getNamespacedName()); + } + } + }); + return false; + } +}