From 6905c2386848a6645db6d0579476087c1c15a953 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Thu, 26 Mar 2026 19:55:13 +0100 Subject: [PATCH 1/5] feat(font)!: rework structure --- .../vulpes/font/FontSymbols.java | 2 +- .../vulpes/font/BitFontSymbol.java | 71 ------------------- .../vulpes/font/FontSymbol.java | 53 ++++++++------ .../vulpes/font/FontSymbolTest.java | 45 +++++++++--- 4 files changed, 66 insertions(+), 105 deletions(-) delete mode 100644 src/main/java/net/onelitefeather/vulpes/font/BitFontSymbol.java diff --git a/src/autogenerated/java/net/onelitefeather/vulpes/font/FontSymbols.java b/src/autogenerated/java/net/onelitefeather/vulpes/font/FontSymbols.java index dadafe0..a25e8ae 100644 --- a/src/autogenerated/java/net/onelitefeather/vulpes/font/FontSymbols.java +++ b/src/autogenerated/java/net/onelitefeather/vulpes/font/FontSymbols.java @@ -4,5 +4,5 @@ * This class is generated in a later step. * Don't add any kind of logic here. */ -public sealed interface FontSymbols permits FontSymbol { +public interface FontSymbols { } diff --git a/src/main/java/net/onelitefeather/vulpes/font/BitFontSymbol.java b/src/main/java/net/onelitefeather/vulpes/font/BitFontSymbol.java deleted file mode 100644 index 2b5f1f0..0000000 --- a/src/main/java/net/onelitefeather/vulpes/font/BitFontSymbol.java +++ /dev/null @@ -1,71 +0,0 @@ -package net.onelitefeather.vulpes.font; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.util.Codec; -import net.onelitefeather.vulpes.gson.KeyGsonAdapter; -import net.onelitefeather.vulpes.registries.Registry; -import net.onelitefeather.vulpes.registries.RegistryFactory; -import net.onelitefeather.vulpes.registries.RegistryResources; -import org.jetbrains.annotations.Unmodifiable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Objects; - -public record BitFontSymbol( - Key key, - int ascent, - int height, - List symbols -) implements FontSymbol { - - private static final Gson GSON = new GsonBuilder() - .registerTypeAdapter(Key.class, KeyGsonAdapter.createMinestom()) - .create(); - private static final Logger LOGGER = LoggerFactory.getLogger(BitFontSymbol.class); - private static final Codec BIT_FONT_SYMBOL_CODEC = new BitFontSymbol.BitFontSymbolCodec(); - - public static final Registry REGISTRY = RegistryFactory.createRegistry(Key.key("vulpes", "fonts"), BitFontSymbol::loadEntries, RegistryResources.BITMAP_FONTS); - - static BitFontSymbol get(Key key) { - return REGISTRY.get(key); - } - - private static @Unmodifiable List loadEntries(InputStream inputStream) { - JsonArray jsonElements = GSON.fromJson(new InputStreamReader(inputStream, StandardCharsets.UTF_8), JsonArray.class); - return jsonElements.asList().stream() - .map(JsonElement::getAsJsonObject) - .map(jsonObject -> { - try { - return BIT_FONT_SYMBOL_CODEC.decode(jsonObject); - } catch (Throwable e) { - LOGGER.error("Failed to decode font symbol: {}", jsonObject, e); - return null; - } - }) - .filter(Objects::nonNull) - .toList(); - } - - static class BitFontSymbolCodec implements Codec { - - @Override - public BitFontSymbol decode(JsonObject encoded) throws Throwable { - return GSON.fromJson(encoded, BitFontSymbol.class); - } - - @Override - public JsonObject encode(BitFontSymbol decoded) throws Throwable { - return GSON.toJsonTree(decoded).getAsJsonObject(); - } - } -} diff --git a/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java b/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java index bba6a51..c16342d 100644 --- a/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java +++ b/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java @@ -1,31 +1,40 @@ package net.onelitefeather.vulpes.font; -import net.onelitefeather.vulpes.registries.VulpesKey; -import org.jetbrains.annotations.UnmodifiableView; +import net.minestom.server.codec.Codec; +import net.minestom.server.codec.StructCodec; import java.util.List; -public sealed interface FontSymbol extends FontSymbols, VulpesKey permits BitFontSymbol { +/** + * Represents a single font provider entry in a Minecraft resource pack font definition. + *

+ * Each instance describes how a set of characters is rendered using a specific font provider + * (e.g. bitmap or unicode) as defined in a {@code font.json}. + * + * @param file the resource location of the font texture (e.g. "namespace:path/to/file.png") + * @param type the type of the font provider (e.g. "bitmap" or "unicode") + * @param ascent the ascent of the glyphs, defining the distance from the baseline to the top + * @param height the default height of the glyphs + * @param chars the characters mapped to this provider, typically defining the glyph layout + */ +public record FontSymbol( + String file, + String type, + int ascent, + int height, + List chars +) { /** - * Returns the symbol ascent. - * - * @return the ascent + * Codec for serializing and deserializing {@link FontSymbol} instances to and from JSON, + * following the structure defined in Minecraft's font provider format. */ - int ascent(); - - /** - * Returns the symbol height. - * - * @return the height - */ - int height(); - - /** - * Returns all symbols for the font. - * - * @return the symbols - */ - @UnmodifiableView - List symbols(); + public static final Codec CODEC = StructCodec.struct( + "file", Codec.STRING, FontSymbol::file, + "type", Codec.STRING, FontSymbol::type, + "ascent", Codec.INT, FontSymbol::ascent, + "height", Codec.INT, FontSymbol::height, + "chars", Codec.STRING.list(), FontSymbol::chars, + FontSymbol::new + ); } diff --git a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java index 52861a2..c5bb297 100644 --- a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java +++ b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java @@ -1,5 +1,7 @@ package net.onelitefeather.vulpes.font; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import net.kyori.adventure.key.Key; import org.junit.jupiter.api.Test; @@ -9,17 +11,38 @@ class FontSymbolTest { + private static final String TEST_JSON = + """ + { + "file": "manis:global/player_ranks/admin.png", + "type": "bitmap", + "ascent": 10, + "height": 10, + "chars": [ + "" + ] + } + """; + @Test - void testFontCreationWithoutFactory() { - Key key = Key.key("vulpes", "test"); - FontSymbol fontSymbol = new BitFontSymbol(key, 10, 20, List.of("\u12ca")); - assertNotNull(fontSymbol); - assertInstanceOf(BitFontSymbol.class, fontSymbol); - - assertEquals(key, fontSymbol.key()); - assertEquals(10, fontSymbol.ascent()); - assertEquals(20, fontSymbol.height()); - assertEquals(1, fontSymbol.symbols().size()); - assertEquals(List.of("\u12ca"), fontSymbol.symbols()); + void testNameTagRead() { + FontSymbol tag = new FontSymbol( + "manis:global/player_ranks/admin.png", + "bitmap", + 10, + 10, + List.of("\uE120") + ); + + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + FontSymbol loadedText = gson.fromJson(TEST_JSON, FontSymbol.class); + assertNotNull(loadedText); + + assertEquals(tag.file(), loadedText.file()); + assertEquals(tag.type(), loadedText.type()); + assertEquals(tag.ascent(), loadedText.ascent()); + assertEquals(tag.height(), loadedText.height()); + assertEquals(1, tag.chars().size()); + assertEquals(tag.chars(), loadedText.chars()); } } From e91a3d957cec194d57ba3c1f3fee2e420b614df7 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Thu, 26 Mar 2026 19:55:24 +0100 Subject: [PATCH 2/5] chore(gson): remove unused gson adapter implementation --- .../vulpes/gson/KeyGsonAdapter.java | 103 ------------------ .../vulpes/gson/package-info.java | 4 - 2 files changed, 107 deletions(-) delete mode 100644 src/main/java/net/onelitefeather/vulpes/gson/KeyGsonAdapter.java delete mode 100644 src/main/java/net/onelitefeather/vulpes/gson/package-info.java diff --git a/src/main/java/net/onelitefeather/vulpes/gson/KeyGsonAdapter.java b/src/main/java/net/onelitefeather/vulpes/gson/KeyGsonAdapter.java deleted file mode 100644 index 83f35fa..0000000 --- a/src/main/java/net/onelitefeather/vulpes/gson/KeyGsonAdapter.java +++ /dev/null @@ -1,103 +0,0 @@ -package net.onelitefeather.vulpes.gson; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import net.kyori.adventure.key.Key; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.util.function.BiFunction; - -/** - * This class is used to serialize and deserialize a {@link Key} object. - * The {@link Key} object is represented as a JSON object with the keys "namespace" and "value". - * The namespace is the namespace of the key and the value is the value of the key. - * The JSON object is represented as follows: - *

- *     {
- *         "namespace": "namespace",
- *         "value": "value"
- *     }
- * 
- * A key can be created by calling the {@link Key#key(String, String)} method. - * Use {@link #create()} to create a default adapter. - * Use {@link #create(BiFunction)} to create an adapter with a custom key creation function. - * Use {@link #createMinestom()} to create an adapter with a custom key({@link Key}) creation function for Minestom. - * - * @since 0.1.0 - * @version 1.1.0 - * @see Key - * @see Key#key(String, String) - * @author TheMeinerLP - */ -public class KeyGsonAdapter extends TypeAdapter { - - private final BiFunction createKeyObject; - - private KeyGsonAdapter(BiFunction createKeyObject) { - this.createKeyObject = createKeyObject; - } - - private KeyGsonAdapter() { - this(Key::key); - } - - /** - * Creates a new instance of the {@link KeyGsonAdapter} with the default key creation function. - * @return the new instance of the {@link KeyGsonAdapter} - */ - @Contract(value = " -> new", pure = true) - public static KeyGsonAdapter create() { - return new KeyGsonAdapter(); - } - - /** - * Creates a new instance of the {@link KeyGsonAdapter} with a custom key creation function. - * @param createKeyObject the custom key creation function - * @return the new instance of the {@link KeyGsonAdapter} - */ - @Contract(value = "_ -> new", pure = true) - public static KeyGsonAdapter create(BiFunction createKeyObject) { - return new KeyGsonAdapter(createKeyObject); - } - - /** - * Creates a new instance of the {@link KeyGsonAdapter} with a custom key) creation function for Minestom. - * @return the new instance of the {@link KeyGsonAdapter} - */ - public static KeyGsonAdapter createMinestom() { - return new KeyGsonAdapter(Key::key); - } - - /** - * {@inheritDoc} - */ - @Override - public void write(JsonWriter out, @Nullable Key value) throws IOException { - if (value == null) return; - out.beginObject(); - out.name("namespace").value(value.namespace()); - out.name("value").value(value.value()); - out.endObject(); - } - - /** - * {@inheritDoc} - */ - @Override - public Key read(JsonReader in) throws IOException { - in.beginObject(); - if (!in.nextName().equals("namespace")) { - throw new IOException("Expected namespace"); - } - var namespace = in.nextString(); - if (!in.nextName().equals("value")) { - throw new IOException("Expected value"); - } - var value = in.nextString(); - in.endObject(); - return this.createKeyObject.apply(namespace, value); - } -} diff --git a/src/main/java/net/onelitefeather/vulpes/gson/package-info.java b/src/main/java/net/onelitefeather/vulpes/gson/package-info.java deleted file mode 100644 index 15a8263..0000000 --- a/src/main/java/net/onelitefeather/vulpes/gson/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NotNullByDefault -package net.onelitefeather.vulpes.gson; - -import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file From 0680fe102c61d0e9d5e1bb079f5e1c0d5a4b17b1 Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sun, 29 Mar 2026 10:39:15 +0200 Subject: [PATCH 3/5] feat(font): add some utility methods --- .../vulpes/font/FontSymbol.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java b/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java index c16342d..d05b9b5 100644 --- a/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java +++ b/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java @@ -2,6 +2,7 @@ import net.minestom.server.codec.Codec; import net.minestom.server.codec.StructCodec; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -37,4 +38,39 @@ public record FontSymbol( "chars", Codec.STRING.list(), FontSymbol::chars, FontSymbol::new ); + + /** + * Checks if this font symbol has any characters defined. + * + * @return true if the chars list is not empty, false otherwise + */ + public boolean hasChars() { + return !chars.isEmpty(); + } + + /** + * Returns the character at the given index. + * + * @param index the index of the character + * @return the character at the given index or null if the index is out of bounds + */ + public @Nullable String getChar(int index) { + if (index < 0 || index >= chars.size()) { + return null; + } + return chars.get(index); + } + + /** + * Returns the overwritten character based on the index, otherwise it returns the default value. + * + * @param index the index of the character + * @param defaultValue the default value + * @return the overwritten character or the default value + */ + public String getCharOr(int index, String defaultValue) { + return (index >= 0 && index < this.chars.size()) + ? this.chars.get(index) + : defaultValue; + } } From 6ef294a8aebf55d42634d26d1b814ccead7c0eab Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sun, 29 Mar 2026 10:39:25 +0200 Subject: [PATCH 4/5] test(font): add new test cases --- .../vulpes/font/FontSymbolTest.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java index c5bb297..444e5d0 100644 --- a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java +++ b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java @@ -2,7 +2,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import net.kyori.adventure.key.Key; import org.junit.jupiter.api.Test; import java.util.List; @@ -24,6 +23,8 @@ class FontSymbolTest { } """; + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + @Test void testNameTagRead() { FontSymbol tag = new FontSymbol( @@ -34,8 +35,7 @@ void testNameTagRead() { List.of("\uE120") ); - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - FontSymbol loadedText = gson.fromJson(TEST_JSON, FontSymbol.class); + FontSymbol loadedText = GSON.fromJson(TEST_JSON, FontSymbol.class); assertNotNull(loadedText); assertEquals(tag.file(), loadedText.file()); @@ -45,4 +45,31 @@ void testNameTagRead() { assertEquals(1, tag.chars().size()); assertEquals(tag.chars(), loadedText.chars()); } + + @Test + void testNameTagMethod() { + FontSymbol loadedText = GSON.fromJson(TEST_JSON, FontSymbol.class); + assertNotNull(loadedText); + + assertTrue(loadedText.hasChars()); + assertEquals("\uE120", loadedText.getChar(0)); + assertEquals("\uE120", loadedText.chars().getFirst()); + + assertNull(loadedText.getChar(1)); + assertNull(loadedText.getChar(-1)); + assertEquals("\uE000", loadedText.getCharOr(1, "\uE000")); + assertEquals("\uE000", loadedText.getCharOr(-1, "\uE000")); + } + + @Test + void testEmptyChars() { + FontSymbol tag = new FontSymbol( + "manis:global/player_ranks/admin.png", + "bitmap", + 10, + 10, + List.of() + ); + assertFalse(tag.hasChars()); + } } From c2befb94f8f35feced9585ad9bbd7e3d676bce1b Mon Sep 17 00:00:00 2001 From: theEvilReaper Date: Sun, 29 Mar 2026 10:39:34 +0200 Subject: [PATCH 5/5] chore(font): remove registry test --- .../vulpes/font/FontRegistryTest.java | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/test/java/net/onelitefeather/vulpes/font/FontRegistryTest.java diff --git a/src/test/java/net/onelitefeather/vulpes/font/FontRegistryTest.java b/src/test/java/net/onelitefeather/vulpes/font/FontRegistryTest.java deleted file mode 100644 index 76e6480..0000000 --- a/src/test/java/net/onelitefeather/vulpes/font/FontRegistryTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.onelitefeather.vulpes.font; - -import net.onelitefeather.vulpes.registries.Registry; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class FontRegistryTest { - - @Test - void testRegistryTest() { - Registry registry = BitFontSymbol.REGISTRY; - assertNotNull(registry); - assertEquals(1, registry.size()); - } -}