diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java b/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java index e388ce6e5..f666527e8 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java @@ -1,12 +1,14 @@ package dev.latvian.mods.kubejs.client; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import dev.latvian.mods.kubejs.event.EventJS; import dev.latvian.mods.kubejs.generator.AssetJsonGenerator; import dev.latvian.mods.kubejs.util.ConsoleJS; import net.minecraft.Util; import net.minecraft.resources.ResourceLocation; +import java.io.IOException; import java.util.function.Consumer; public class GenerateClientAssetsEventJS extends EventJS { @@ -38,4 +40,8 @@ public void addMultipartBlockState(ResourceLocation id, Consumer map) { } } + @Override + protected boolean forgetFile(String path) { + return path.endsWith(".png") || path.endsWith(".ogg"); + } + @Override protected boolean skipFile(GeneratedData data) { return data.id().getPath().startsWith("lang/"); diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/StencilTexture.java b/common/src/main/java/dev/latvian/mods/kubejs/client/StencilTexture.java new file mode 100644 index 000000000..56fea7452 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/StencilTexture.java @@ -0,0 +1,57 @@ +package dev.latvian.mods.kubejs.client; + +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; + +public class StencilTexture { + public int width; + public int height; + public int[] pixels; + public byte[] mcmeta; + + public StencilTexture(BufferedImage img, byte[] mcmeta) { + this.width = img.getWidth(); + this.height = img.getHeight(); + this.pixels = new int[width * height]; + img.getRGB(0, 0, width, height, pixels, 0, width); + this.mcmeta = mcmeta; + } + + public byte[] create(JsonObject colors) { + var colorMap = new Int2IntArrayMap(colors.size()); + + for (var entry : colors.entrySet()) { + var k = entry.getKey(); + var v = entry.getValue().getAsString(); + int col = Integer.parseUnsignedInt(v.startsWith("#") ? v.substring(1) : v, 16); + + if ((col & 0xFF000000) == 0) { + col |= 0xFF000000; + } + + colorMap.put((Integer.parseUnsignedInt(k.startsWith("#") ? k.substring(1) : k, 16)) & 0xFFFFFF, col); + } + + int[] result = new int[pixels.length]; + + for (int i = 0; i < pixels.length; i++) { + result[i] = ((pixels[i] & 0xFF000000) == 0) ? 0 : colorMap.getOrDefault(pixels[i] & 0xFFFFFF, pixels[i]); + } + + var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + img.setRGB(0, 0, width, height, result, 0, width); + var out = new ByteArrayOutputStream(); + + try { + ImageIO.write(img, "png", out); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + return out.toByteArray(); + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java index dc95d77b1..562648590 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java @@ -1,19 +1,30 @@ package dev.latvian.mods.kubejs.generator; +import com.google.gson.JsonObject; +import dev.latvian.mods.kubejs.KubeJSPaths; import dev.latvian.mods.kubejs.client.ModelGenerator; import dev.latvian.mods.kubejs.client.MultipartBlockStateGenerator; +import dev.latvian.mods.kubejs.client.StencilTexture; import dev.latvian.mods.kubejs.client.VariantBlockStateGenerator; import dev.latvian.mods.kubejs.script.data.GeneratedData; import dev.latvian.mods.kubejs.util.ConsoleJS; import net.minecraft.Util; import net.minecraft.resources.ResourceLocation; +import javax.imageio.ImageIO; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; -public class AssetJsonGenerator extends JsonGenerator { +public class AssetJsonGenerator extends ResourceGenerator { + private final Map stencils; + public AssetJsonGenerator(Map m) { super(ConsoleJS.CLIENT, m); + this.stencils = new HashMap<>(); } public void blockState(ResourceLocation id, Consumer consumer) { @@ -39,4 +50,35 @@ public void itemModel(ResourceLocation id, Consumer consumer) { public static ResourceLocation asItemModelLocation(ResourceLocation id) { return new ResourceLocation(id.getNamespace(), "models/item/" + id.getPath()); } + + public void stencil(ResourceLocation target, String stencil, JsonObject colors) throws IOException { + var st = stencils.get(stencil); + + if (st == null) { + var path = KubeJSPaths.ASSETS.resolve("kubejs/textures/stencil/" + stencil + ".png"); + + if (Files.notExists(path)) { + throw new IllegalArgumentException("Stencil file 'kubejs/assets/kubejs/textures/stencil/'" + stencil + ".png' not found!"); + } + + try (var in = new BufferedInputStream(Files.newInputStream(path))) { + var metaPath = KubeJSPaths.ASSETS.resolve("kubejs/textures/stencil/" + stencil + ".png.mcmeta"); + byte[] meta = null; + + if (Files.exists(metaPath)) { + meta = Files.readAllBytes(metaPath); + } + + st = new StencilTexture(ImageIO.read(in), meta); + stencils.put(stencil, st); + } + } + + var st1 = st; + add(new ResourceLocation(target.getNamespace(), "textures/" + target.getPath() + ".png"), () -> st1.create(colors), true); + + if (st.mcmeta != null) { + add(new ResourceLocation(target.getNamespace(), "textures/" + target.getPath() + ".png.mcmeta"), () -> st1.mcmeta, false); + } + } } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/generator/DataJsonGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/generator/DataJsonGenerator.java index 924de3eaf..511c0eaa3 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/generator/DataJsonGenerator.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/generator/DataJsonGenerator.java @@ -6,7 +6,7 @@ import java.util.Map; -public class DataJsonGenerator extends JsonGenerator { +public class DataJsonGenerator extends ResourceGenerator { public DataJsonGenerator(Map m) { super(ConsoleJS.SERVER, m); } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/generator/JsonGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/generator/ResourceGenerator.java similarity index 76% rename from common/src/main/java/dev/latvian/mods/kubejs/generator/JsonGenerator.java rename to common/src/main/java/dev/latvian/mods/kubejs/generator/ResourceGenerator.java index d0fbc462d..31677cb9b 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/generator/JsonGenerator.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/generator/ResourceGenerator.java @@ -11,17 +11,21 @@ import java.util.Map; import java.util.function.Supplier; -public class JsonGenerator { +public class ResourceGenerator { private final ConsoleJS console; private final Map map; - public JsonGenerator(ConsoleJS c, Map m) { + public ResourceGenerator(ConsoleJS c, Map m) { console = c; map = m; } + public void add(ResourceLocation id, Supplier data, boolean alwaysForget) { + map.put(id, new GeneratedData(id, Lazy.of(data), alwaysForget)); + } + public void add(ResourceLocation id, Supplier data) { - map.put(id, new GeneratedData(id, Lazy.of(data))); + add(id, data, false); } public void json(ResourceLocation id, JsonElement json) { diff --git a/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java b/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java index 53e5aef88..6ba01ea5b 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedData.java @@ -13,8 +13,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; -public record GeneratedData(ResourceLocation id, Lazy data) implements IoSupplier { - public static final GeneratedData INTERNAL_RELOAD = new GeneratedData(KubeJS.id("__internal.reload"), Lazy.of(() -> new byte[0])); +public record GeneratedData(ResourceLocation id, Lazy data, boolean alwaysForget) implements IoSupplier { + public static final GeneratedData INTERNAL_RELOAD = new GeneratedData(KubeJS.id("__internal.reload"), Lazy.of(() -> new byte[0]), false); public static final GeneratedData PACK_META = new GeneratedData(KubeJS.id("pack.mcmeta"), Lazy.of(() -> { var json = new JsonObject(); @@ -23,7 +23,7 @@ public record GeneratedData(ResourceLocation id, Lazy data) implements I pack.addProperty("pack_format", 15); json.add("pack", pack); return json.toString().getBytes(StandardCharsets.UTF_8); - })); + }), false); public static final GeneratedData PACK_ICON = new GeneratedData(KubeJS.id("textures/kubejs_logo.png"), Lazy.of(() -> { try { @@ -32,12 +32,18 @@ public record GeneratedData(ResourceLocation id, Lazy data) implements I ex.printStackTrace(); return new byte[0]; } - })); + }), true); @Override @NotNull public InputStream get() { - return new ByteArrayInputStream(data.get()); + var in = new ByteArrayInputStream(data.get()); + + if (alwaysForget) { + data.forget(); + } + + return in; } @Override diff --git a/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedResourcePack.java b/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedResourcePack.java index 812cce10e..3e3f96a41 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedResourcePack.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/script/data/GeneratedResourcePack.java @@ -115,7 +115,7 @@ public Map getGenerated() { ex.printStackTrace(); return new byte[0]; } - })); + }), forgetFile(pathStr)); if (debug) { KubeJS.LOGGER.info("- File found: '" + data.id() + "' (" + data.data().get().length + " bytes)"); @@ -148,6 +148,10 @@ public Map getGenerated() { return generated; } + protected boolean forgetFile(String path) { + return false; + } + protected boolean skipFile(GeneratedData data) { return false; }