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..d05b9b5 100644 --- a/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java +++ b/src/main/java/net/onelitefeather/vulpes/font/FontSymbol.java @@ -1,31 +1,76 @@ 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 org.jetbrains.annotations.Nullable; 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. + * Codec for serializing and deserializing {@link FontSymbol} instances to and from JSON, + * following the structure defined in Minecraft's font provider format. + */ + 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 + ); + + /** + * Checks if this font symbol has any characters defined. * - * @return the ascent + * @return true if the chars list is not empty, false otherwise */ - int ascent(); + public boolean hasChars() { + return !chars.isEmpty(); + } /** - * Returns the symbol height. + * Returns the character at the given index. * - * @return the height + * @param index the index of the character + * @return the character at the given index or null if the index is out of bounds */ - int height(); + public @Nullable String getChar(int index) { + if (index < 0 || index >= chars.size()) { + return null; + } + return chars.get(index); + } /** - * Returns all symbols for the font. + * Returns the overwritten character based on the index, otherwise it returns the default value. * - * @return the symbols + * @param index the index of the character + * @param defaultValue the default value + * @return the overwritten character or the default value */ - @UnmodifiableView - List symbols(); + public String getCharOr(int index, String defaultValue) { + return (index >= 0 && index < this.chars.size()) + ? this.chars.get(index) + : defaultValue; + } } 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 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()); - } -} diff --git a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java index 52861a2..444e5d0 100644 --- a/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java +++ b/src/test/java/net/onelitefeather/vulpes/font/FontSymbolTest.java @@ -1,6 +1,7 @@ package net.onelitefeather.vulpes.font; -import net.kyori.adventure.key.Key; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import org.junit.jupiter.api.Test; import java.util.List; @@ -9,17 +10,66 @@ class FontSymbolTest { + private static final String TEST_JSON = + """ + { + "file": "manis:global/player_ranks/admin.png", + "type": "bitmap", + "ascent": 10, + "height": 10, + "chars": [ + "" + ] + } + """; + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + @Test + void testNameTagRead() { + FontSymbol tag = new FontSymbol( + "manis:global/player_ranks/admin.png", + "bitmap", + 10, + 10, + List.of("\uE120") + ); + + 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()); + } + + @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 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 testEmptyChars() { + FontSymbol tag = new FontSymbol( + "manis:global/player_ranks/admin.png", + "bitmap", + 10, + 10, + List.of() + ); + assertFalse(tag.hasChars()); } }