diff options
Diffstat (limited to 'src/main')
8 files changed, 85 insertions, 377 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java index fd0a12a..113464c 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/client/BackgroundPainter.java @@ -4,8 +4,14 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import io.github.cottonmc.cotton.gui.impl.LibGuiCommon; +import io.github.cottonmc.cotton.gui.impl.client.NinePatchTextureRendererImpl; import io.github.cottonmc.cotton.gui.widget.WItemSlot; import io.github.cottonmc.cotton.gui.widget.WWidget; +import io.github.cottonmc.cotton.gui.widget.data.Texture; +import juuxel.libninepatch.NinePatch; +import juuxel.libninepatch.TextureRegion; + +import java.util.function.Consumer; /** * Background painters are used to paint the background of a widget. @@ -112,28 +118,39 @@ public interface BackgroundPainter { /** * Creates a new nine-patch background painter. * - * <p>This method is equivalent to {@code new NinePatch(texture)}. + * <p>The resulting painter has a corner size of 4 px and a corner UV of 0.25. * * @param texture the background painter texture * @return a new nine-patch background painter * @since 1.5.0 */ - public static NinePatch createNinePatch(Identifier texture) { - return new NinePatch(texture); + public static BackgroundPainter createNinePatch(Identifier texture) { + return createNinePatch(new Texture(texture), builder -> builder.cornerSize(4).cornerUv(0.25f)); } /** - * Creates a new nine-patch background painter with a custom padding. - * - * <p>This method is equivalent to {@code new NinePatch(texture).setPadding(padding)}. + * Creates a new nine-patch background painter with a custom configuration. * - * @param texture the background painter texture - * @param padding the padding of the painter - * @return a new nine-patch background painter - * @since 1.5.0 + * @param texture the background painter texture + * @param configurator a consumer that configures the {@link NinePatch.Builder} + * @return the created nine-patch background painter + * @since 4.0.0 + * @see NinePatch + * @see NinePatch.Builder */ - public static NinePatch createNinePatch(Identifier texture, int padding) { - return new NinePatch(texture).setPadding(padding); + public static BackgroundPainter createNinePatch(Texture texture, Consumer<NinePatch.Builder<Identifier>> configurator) { + TextureRegion<Identifier> region = new TextureRegion<>(texture.image, texture.u1, texture.v1, texture.u2, texture.v2); + var builder = NinePatch.builder(region); + configurator.accept(builder); + var ninePatch = builder.build(); + return (matrices, left, top, panel) -> { + try (NinePatchTextureRendererImpl renderer = NinePatchTextureRendererImpl.bind(matrices)) { + matrices.push(); + matrices.translate(left, top, 0); + ninePatch.draw(renderer, panel.getWidth(), panel.getHeight()); + matrices.pop(); + } + }; } /** diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatch.java b/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatch.java deleted file mode 100644 index 29b2bea..0000000 --- a/src/main/java/io/github/cottonmc/cotton/gui/client/NinePatch.java +++ /dev/null @@ -1,250 +0,0 @@ -package io.github.cottonmc.cotton.gui.client; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.MathHelper; - -import io.github.cottonmc.cotton.gui.impl.client.NinePatchInternals; -import io.github.cottonmc.cotton.gui.widget.WWidget; -import org.jetbrains.annotations.Nullable; - -/** - * The nine-patch background painter paints rectangles using a nine-patch texture. - * - * <p>Nine-patch textures are separated into nine sections: four corners, four edges and a center part. - * The edges and the center are either tiled or stretched, depending on the {@linkplain Mode mode}, - * to fill the area between the corners. By default, the texture mode is loaded from the texture metadata. - * The default mode for that is {@link Mode#STRETCHING}. - * - * <p>{@code NinePatch} painters have a customizable padding that can be applied. - * For example, a GUI panel for a container block might have a padding of 8 pixels, like {@link BackgroundPainter#VANILLA}. - * You can set the padding using {@link NinePatch#setPadding(int)}. - * - * <h2>Nine-patch metadata</h2> - * You can specify metadata for a nine-patch texture in a resource pack by creating a metadata file. - * Metadata files can currently specify the filling mode of the painter that paints the texture. - * <p>The metadata file for a texture has to be placed in the same directory as the texture. - * The file name must be {@code X.9patch} where X is the texture file name (including .png). - * <p>Metadata files use {@linkplain java.util.Properties the .properties format} with the following keys: - * <table border="1"> - * <caption>Properties</caption> - * <tr> - * <th>Key</th> - * <th>Value</th> - * <th>Description</th> - * </tr> - * <tr> - * <td>{@code mode}</td> - * <td>{@link Mode#STRETCHING stretching} | {@link Mode#TILING tiling}</td> - * <td>The texture filling mode</td> - * </tr> - * </table> - * - * @since 1.5.0 - */ -@Environment(EnvType.CLIENT) -public class NinePatch implements BackgroundPainter { - private final Identifier texture; - private final int cornerSize; - private final float cornerUv; - private int topPadding = 0; - private int leftPadding = 0; - private int bottomPadding = 0; - private int rightPadding = 0; - private Mode mode = null; - - /** - * Creates a nine-patch background painter with 4 px corners and a 0.25 corner UV. - * - * @param texture the texture ID - */ - public NinePatch(Identifier texture) { - this(texture, 4, 0.25f); - } - - /** - * Creates a nine-patch background painter. - * - * @param texture the texture ID - * @param cornerSize the size of the corners on the screen - * @param cornerUv the fraction of the corners from the whole texture - */ - public NinePatch(Identifier texture, int cornerSize, float cornerUv) { - this.texture = texture; - this.cornerSize = cornerSize; - this.cornerUv = cornerUv; - } - - public int getTopPadding() { - return topPadding; - } - - public NinePatch setTopPadding(int topPadding) { - this.topPadding = topPadding; - return this; - } - - public int getLeftPadding() { - return leftPadding; - } - - public NinePatch setLeftPadding(int leftPadding) { - this.leftPadding = leftPadding; - return this; - } - - public int getBottomPadding() { - return bottomPadding; - } - - public NinePatch setBottomPadding(int bottomPadding) { - this.bottomPadding = bottomPadding; - return this; - } - - public int getRightPadding() { - return rightPadding; - } - - public NinePatch setRightPadding(int rightPadding) { - this.rightPadding = rightPadding; - return this; - } - - public NinePatch setPadding(int padding) { - this.topPadding = this.leftPadding = this.bottomPadding = this.rightPadding = padding; - return this; - } - - public NinePatch setPadding(int vertical, int horizontal) { - this.topPadding = this.bottomPadding = vertical; - this.leftPadding = this.rightPadding = horizontal; - return this; - } - - public NinePatch setPadding(int topPadding, int leftPadding, int bottomPadding, int rightPadding) { - this.topPadding = topPadding; - this.leftPadding = leftPadding; - this.bottomPadding = bottomPadding; - this.rightPadding = rightPadding; - - return this; - } - - public Identifier getTexture() { - return texture; - } - - public int getCornerSize() { - return cornerSize; - } - - @Nullable - public Mode getMode() { - return mode; - } - - /** - * Sets the {@linkplain Mode mode} of this painter to the specified mode. - * <p>If the {@code mode} is not null, it will override the one specified in the texture metadata. - * A null mode uses the texture metadata. - */ - public NinePatch setMode(@Nullable Mode mode) { - this.mode = mode; - return this; - } - - @Override - public void paintBackground(MatrixStack matrices, int left, int top, WWidget panel) { - int width = panel.getWidth() + leftPadding + rightPadding; - int height = panel.getHeight() + topPadding + bottomPadding; - left = left - leftPadding; - top = top - topPadding; - int x1 = left + cornerSize; - int x2 = left + width - cornerSize; - int y1 = top + cornerSize; - int y2 = top + height - cornerSize; - float uv1 = cornerUv; - float uv2 = 1.0f - cornerUv; - Mode mode = this.mode != null ? this.mode : NinePatchInternals.MetadataLoader.INSTANCE.getProperties(texture).getMode(); - - ScreenDrawing.texturedRect(matrices, left, top, cornerSize, cornerSize, texture, 0, 0, uv1, uv1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x2, top, cornerSize, cornerSize, texture, uv2, 0, 1, uv1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, left, y2, cornerSize, cornerSize, texture, 0, uv2, uv1, 1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x2, y2, cornerSize, cornerSize, texture, uv2, uv2, 1, 1, 0xFF_FFFFFF); - - if (mode == Mode.TILING) { - int tileSize = (int) (cornerSize / cornerUv - 2 * cornerSize); - int widthLeft = width - 2 * cornerSize; - int heightLeft = height - 2 * cornerSize; - int tileCountX = MathHelper.ceil((float) widthLeft / tileSize); - int tileCountY = MathHelper.ceil((float) heightLeft / tileSize); - for (int i = 0; i < tileCountX; i++) { - float px = 1 / 16f; - int tileWidth = Math.min(widthLeft, tileSize); - float uo = (tileSize - tileWidth) * px; // Used to remove unnecessary pixels on the X axis - - ScreenDrawing.texturedRect(matrices, x1 + i * tileSize, top, tileWidth, cornerSize, texture, uv1, 0, uv2 - uo, uv1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x1 + i * tileSize, y2, tileWidth, cornerSize, texture, uv1, uv2, uv2 - uo, 1, 0xFF_FFFFFF); - - // Reset the height left each time the Y is looped - heightLeft = height - 2 * cornerSize; - - for (int j = 0; j < tileCountY; j++) { - int tileHeight = Math.min(heightLeft, tileSize); - float vo = (tileSize - tileHeight) * px; // Used to remove unnecessary pixels on the Y axis - - ScreenDrawing.texturedRect(matrices, left, y1 + j * tileSize, cornerSize, tileHeight, texture, 0, uv1, uv1, uv2 - vo, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x2, y1 + j * tileSize, cornerSize, tileHeight, texture, uv2, uv1, 1, uv2 - vo, 0xFF_FFFFFF); - - ScreenDrawing.texturedRect(matrices, x1 + i * tileSize, y1 + j * tileSize, tileWidth, tileHeight, texture, uv1, uv1, uv2 - uo, uv2 - vo, 0xFF_FFFFFF); - heightLeft -= tileSize; - } - widthLeft -= tileSize; - } - } else { - ScreenDrawing.texturedRect(matrices, x1, top, width - 2 * cornerSize, cornerSize, texture, uv1, 0, uv2, uv1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, left, y1, cornerSize, height - 2 * cornerSize, texture, 0, uv1, uv1, uv2, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x1, y2, width - 2 * cornerSize, cornerSize, texture, uv1, uv2, uv2, 1, 0xFF_FFFFFF); - ScreenDrawing.texturedRect(matrices, x2, y1, cornerSize, height - 2 * cornerSize, texture, uv2, uv1, 1, uv2, 0xFF_FFFFFF); - - ScreenDrawing.texturedRect(matrices, x1, y1, width - 2 * cornerSize, height - 2 * cornerSize, texture, uv1, uv1, uv2, uv2, 0xFF_FFFFFF); - } - } - - /** - * The mode of a nine-patch painter defines how it fills the area between the corners. - */ - public enum Mode { - /** - * The texture is stretched to fill the edges and the center. - * This is the default mode. - */ - STRETCHING, - - /** - * The texture is tiled to fill the edges and the center. - */ - TILING; - - /** - * Deserializes a nine-patch mode from a string. - * - * @param str the mode string - * @return the mode, or null if the string is invalid - * @since 4.0.0 - */ - @Nullable - public static Mode fromString(String str) { - if (str == null) return null; - - if (str.equalsIgnoreCase("stretching")) return STRETCHING; - if (str.equalsIgnoreCase("tiling")) return TILING; - - return null; - } - } - -} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/LibGuiClient.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/LibGuiClient.java index acd8c5e..6bc69fe 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/LibGuiClient.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/LibGuiClient.java @@ -2,9 +2,7 @@ package io.github.cottonmc.cotton.gui.impl.client; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.resource.ResourceType; import blue.endless.jankson.Jankson; import blue.endless.jankson.JsonElement; @@ -29,8 +27,6 @@ public class LibGuiClient implements ClientModInitializer { public void onInitializeClient() { config = loadConfig(); - ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(NinePatchInternals.MetadataLoader.INSTANCE); - ClientPlayNetworking.registerGlobalReceiver(ScreenNetworkingImpl.SCREEN_MESSAGE_S2C, (client, networkHandler, buf, responseSender) -> { ScreenNetworkingImpl.handle(client, client.player, buf); }); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchInternals.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchInternals.java deleted file mode 100644 index ddb4301..0000000 --- a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchInternals.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.cottonmc.cotton.gui.impl.client; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; -import net.minecraft.resource.Resource; -import net.minecraft.resource.ResourceManager; -import net.minecraft.resource.SinglePreparationResourceReloader; -import net.minecraft.util.Identifier; -import net.minecraft.util.profiler.Profiler; - -import io.github.cottonmc.cotton.gui.client.NinePatch; -import io.github.cottonmc.cotton.gui.impl.LibGuiCommon; - -import java.io.InputStream; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -@Environment(EnvType.CLIENT) -public final class NinePatchInternals { - @Environment(EnvType.CLIENT) - public static class TextureProperties { - public static final TextureProperties DEFAULT = new TextureProperties(NinePatch.Mode.STRETCHING); - - private final NinePatch.Mode mode; - - public TextureProperties(NinePatch.Mode mode) { - this.mode = mode; - } - - public NinePatch.Mode getMode() { - return mode; - } - } - - @Environment(EnvType.CLIENT) - public static class MetadataLoader extends SinglePreparationResourceReloader<Map<Identifier, Properties>> implements IdentifiableResourceReloadListener { - public static final MetadataLoader INSTANCE = new MetadataLoader(); - - private static final Identifier ID = new Identifier(LibGuiCommon.MOD_ID, "9patch_metadata"); - private static final String SUFFIX = ".9patch"; - - private Map<Identifier, TextureProperties> properties = Collections.emptyMap(); - - public TextureProperties getProperties(Identifier texture) { - return properties.getOrDefault(texture, TextureProperties.DEFAULT); - } - - @Override - public Identifier getFabricId() { - return ID; - } - - @Override - protected Map<Identifier, Properties> prepare(ResourceManager manager, Profiler profiler) { - Collection<Identifier> ids = manager.findResources("textures", s -> s.endsWith(SUFFIX)); - Map<Identifier, Properties> result = new HashMap<>(); - - for (Identifier input : ids) { - try (Resource resource = manager.getResource(input); - InputStream stream = resource.getInputStream()) { - Properties props = new Properties(); - props.load(stream); - Identifier textureId = new Identifier(input.getNamespace(), input.getPath().substring(0, input.getPath().length() - SUFFIX.length())); - result.put(textureId, props); - } catch (Exception e) { - LibGuiClient.logger.error("Error while loading metadata file {}, skipping...", input, e); - } - } - - return result; - } - - @Override - protected void apply(Map<Identifier, Properties> meta, ResourceManager manager, Profiler profiler) { - properties = new HashMap<>(); - for (Map.Entry<Identifier, Properties> entry : meta.entrySet()) { - Identifier id = entry.getKey(); - Properties props = entry.getValue(); - - NinePatch.Mode mode = TextureProperties.DEFAULT.getMode(); -// float cornerUv = TextureProperties.DEFAULT.getCornerUv(); - - if (props.containsKey("mode")) { - String modeStr = props.getProperty("mode"); - mode = NinePatch.Mode.fromString(modeStr); - if (mode == null) { - LibGuiClient.logger.error("Invalid mode '{}' in nine-patch metadata file for texture {}", modeStr, id); - continue; - } - } - -// if (props.containsKey("cornerUv")) { -// cornerUv = Float.parseFloat(props.getProperty("cornerUv")); -// } - - TextureProperties texProperties = new TextureProperties(mode); - properties.put(id, texProperties); - } - } - } -} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchTextureRendererImpl.java b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchTextureRendererImpl.java new file mode 100644 index 0000000..9395c5f --- /dev/null +++ b/src/main/java/io/github/cottonmc/cotton/gui/impl/client/NinePatchTextureRendererImpl.java @@ -0,0 +1,36 @@ +package io.github.cottonmc.cotton.gui.impl.client; + +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; + +import io.github.cottonmc.cotton.gui.client.ScreenDrawing; +import juuxel.libninepatch.TextureRenderer; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * An implementation of LibNinePatch's {@link TextureRenderer} for identifiers. + */ +public final class NinePatchTextureRendererImpl implements TextureRenderer<Identifier>, AutoCloseable { + private static final NinePatchTextureRendererImpl INSTANCE = new NinePatchTextureRendererImpl(); + private final Deque<MatrixStack> matrixStackStack = new ArrayDeque<>(); + + private NinePatchTextureRendererImpl() {} + + // TODO: Replace this with a context system in LNP + public static NinePatchTextureRendererImpl bind(MatrixStack matrices) { + INSTANCE.matrixStackStack.push(matrices); + return INSTANCE; + } + + @Override + public void draw(Identifier texture, int x, int y, int width, int height, float u1, float v1, float u2, float v2) { + ScreenDrawing.texturedRect(matrixStackStack.peek(), x, y, width, height, texture, u1, v1, u2, v2, 0xFF_FFFFFF); + } + + @Override + public void close() { + matrixStackStack.pop(); + } +} diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java index 2ba08be..fab8941 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WTabPanel.java @@ -38,7 +38,6 @@ public class WTabPanel extends WPanel { private static final int TAB_PADDING = 4; private static final int TAB_WIDTH = 28; private static final int TAB_HEIGHT = 30; - private static final int PANEL_PADDING = 8; // The padding of BackgroundPainter.VANILLA private static final int ICON_SIZE = 16; private final WBox tabRibbon = new WBox(Axis.HORIZONTAL).setSpacing(1); private final List<WTab> tabWidgets = new ArrayList<>(); @@ -49,7 +48,7 @@ public class WTabPanel extends WPanel { */ public WTabPanel() { add(tabRibbon, 0, 0); - add(mainPanel, PANEL_PADDING, TAB_HEIGHT + PANEL_PADDING); + add(mainPanel, 0, TAB_HEIGHT); } private void add(WWidget widget, int x, int y) { @@ -327,11 +326,23 @@ public class WTabPanel extends WPanel { } } + // Make the tab a bit higher... + if (selected) { + height += 2; + y -= 2; + } + (selected ? Painters.SELECTED_TAB : Painters.UNSELECTED_TAB).paintBackground(matrices, x, y, this); if (isFocused()) { (selected ? Painters.SELECTED_TAB_FOCUS_BORDER : Painters.UNSELECTED_TAB_FOCUS_BORDER).paintBackground(matrices, x, y, this); } + // ...and revert the size change here + if (selected) { + height -= 2; + y += 2; + } + int iconX = 6; if (title != null) { @@ -367,8 +378,8 @@ public class WTabPanel extends WPanel { @Environment(EnvType.CLIENT) final static class Painters { static final BackgroundPainter SELECTED_TAB = BackgroundPainter.createLightDarkVariants( - BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/selected_light.png")).setTopPadding(2), - BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/selected_dark.png")).setTopPadding(2) + BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/selected_light.png")), + BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/selected_dark.png")) ); static final BackgroundPainter UNSELECTED_TAB = BackgroundPainter.createLightDarkVariants( @@ -376,7 +387,7 @@ public class WTabPanel extends WPanel { BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/unselected_dark.png")) ); - static final BackgroundPainter SELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/focus.png")).setTopPadding(2); + static final BackgroundPainter SELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/focus.png")); static final BackgroundPainter UNSELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier(LibGuiCommon.MOD_ID, "textures/widget/tab/focus.png")); } } diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/icon/ItemIcon.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/icon/ItemIcon.java index 2575d36..f8455ba 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/icon/ItemIcon.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/icon/ItemIcon.java @@ -53,7 +53,9 @@ public class ItemIcon implements Icon { modelViewMatrices.push(); modelViewMatrices.translate(x, y, 0); modelViewMatrices.scale(scale, scale, 1); + RenderSystem.applyModelViewMatrix(); renderer.renderInGui(stack, 0, 0); modelViewMatrices.pop(); + RenderSystem.applyModelViewMatrix(); } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9f430dc..6b2cc35 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -30,7 +30,8 @@ "fabric": "*", "minecraft": ">=1.16.5", "jankson": "^3.0.0", - "fabric-networking-api-v1": "^1.0.0" + "fabric-networking-api-v1": "^1.0.0", + "libninepatch": "^1.0.0" }, "suggests": { "flamingo": "*" |