diff --git a/api/src/main/java/ru/dragonestia/msb3/api/ServerBootstrap.java b/api/src/main/java/ru/dragonestia/msb3/api/ServerBootstrap.java index 3f408bc..2c03545 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/ServerBootstrap.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/ServerBootstrap.java @@ -10,6 +10,8 @@ import java.net.InetSocketAddress; @Log4j2 public class ServerBootstrap { + public static final ClassLoader CLASS_LOADER = ServerBootstrap.class.getClassLoader(); + private static final String[] LOGO = { "==============================================", "Minestom Server Base v3 - by ScarletRedMan", diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/GlyphConstants.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/GlyphConstants.java new file mode 100644 index 0000000..2cfc056 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/GlyphConstants.java @@ -0,0 +1,7 @@ +package ru.dragonestia.msb3.api.glyph; + +public final class GlyphConstants { + + public static final int CHEST_GUI_WIDTH = 176; + public static final int DEFAULT_CHAT_WIDTH = 320; +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ArbitraryCharacterFactory.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ArbitraryCharacterFactory.java new file mode 100644 index 0000000..d380843 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ArbitraryCharacterFactory.java @@ -0,0 +1,8 @@ +package ru.dragonestia.msb3.api.glyph.compile; + +import org.jetbrains.annotations.NotNull; + +public interface ArbitraryCharacterFactory { + + @NotNull Character nextCharacter() throws IllegalStateException; +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultArbitraryCharacterFactory.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultArbitraryCharacterFactory.java new file mode 100644 index 0000000..8462007 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultArbitraryCharacterFactory.java @@ -0,0 +1,51 @@ +package ru.dragonestia.msb3.api.glyph.compile; + +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class DefaultArbitraryCharacterFactory implements ArbitraryCharacterFactory { + + private final static Set reservedCharacters = new HashSet<>(); + + static { + for (char c = 'a'; c <= 'z'; c++) reservedCharacters.add(c); + for (char c = 'A'; c <= 'Z'; c++) reservedCharacters.add(c); + for (char c = 'а'; c <= 'я'; c++) reservedCharacters.add(c); + for (char c = 'А'; c <= 'Я'; c++) reservedCharacters.add(c); + for (char c = '0'; c <= '9'; c++) reservedCharacters.add(c); + + reservedCharacters.addAll(Arrays.asList( + '!', '?', ':', '$', + ';', '#', '@', '%', + '^', '&', '*', '(', + ')', '_', '-', '+', + '/', '\\', '№', '"', + '\'', '{', '}', '[', + ']', '~', '`', '<', '>', + ',', '.', '|', '\n', '\r', + '\b', '\f', '\t', ' ', + 'ё', 'Ё', '=') + ); + } + + private char currentChar = '\uA201'; + + @Override + public @NotNull Character nextCharacter() throws IllegalStateException { + if (currentChar == Character.MAX_VALUE) { + throw new IllegalStateException("Characters range exceeded"); + } + + do { + currentChar++; + } while (!isCharacterAllowed(currentChar)); + return currentChar; + } + + private boolean isCharacterAllowed(char c) { + return !reservedCharacters.contains(c); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultGlyphCompiler.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultGlyphCompiler.java new file mode 100644 index 0000000..3bdfe65 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/DefaultGlyphCompiler.java @@ -0,0 +1,45 @@ +package ru.dragonestia.msb3.api.glyph.compile; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import team.unnamed.creative.font.Font; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.part.ResourcePackPart; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class DefaultGlyphCompiler implements GlyphCompiler { + + @Override + public @NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull Collection<@NotNull ResourceProducer> producers) { + Set fileResources = new HashSet<>(); + + Set fontKeys = producers + .stream() + .map(ResourceProducer::fontKey) + .collect(Collectors.toUnmodifiableSet()); + + for (Key key : fontKeys) { + List fontProviders = new ArrayList<>(); + ArbitraryCharacterFactory characterFactory = new DefaultArbitraryCharacterFactory(); + for (ResourceProducer producer : producers) { + if (producer.fontKey().equals(key)) { + producer.produceResources(characterFactory); + // Add font providers for current font key + fontProviders.addAll(producer.fontProviders()); + // Add textures to common set with resources + fileResources.addAll(producer.textures()); + } + } + fileResources.add(Font.font(key, fontProviders)); + } + + return fileResources; + } + + Iterable toIterable(final Stream stream) { + return stream::iterator; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/GlyphCompiler.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/GlyphCompiler.java new file mode 100644 index 0000000..2e95249 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/GlyphCompiler.java @@ -0,0 +1,20 @@ +package ru.dragonestia.msb3.api.glyph.compile; + +import org.jetbrains.annotations.NotNull; +import team.unnamed.creative.part.ResourcePackPart; + +import java.util.Arrays; +import java.util.Collection; + +public interface GlyphCompiler { + + static GlyphCompiler instance() { + return new DefaultGlyphCompiler(); + } + + @NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull Collection<@NotNull ResourceProducer> resourceProducerCollection); + + default @NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull ResourceProducer... resourceProducer) { + return compile(Arrays.asList(resourceProducer)); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ResourceProducer.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ResourceProducer.java new file mode 100644 index 0000000..ad6d90b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/compile/ResourceProducer.java @@ -0,0 +1,26 @@ +package ru.dragonestia.msb3.api.glyph.compile; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.texture.Texture; + +import java.util.Collection; +import java.util.Collections; + +public interface ResourceProducer { + + @NotNull Key fontKey(); + + boolean produced(); + + void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException; + + @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException; + + default @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException { + return Collections.emptyList(); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java new file mode 100644 index 0000000..2f1640c --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/font/GlyphFont.java @@ -0,0 +1,55 @@ +package ru.dragonestia.msb3.api.glyph.font; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.ServerBootstrap; +import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.space.mojang.MojangSpacesGlyph; +import ru.dragonestia.msb3.api.util.ResourceFromJar; +import team.unnamed.creative.base.Writable; +import team.unnamed.creative.part.ResourcePackPart; +import team.unnamed.creative.texture.Texture; + +import java.util.Collection; +import java.util.List; + +public class GlyphFont { + + public static final SpacesGlyphResourceProducer SPACES = MojangSpacesGlyph.create(); + private static final Key MINECRAFT_FONT_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "minecraft_font"); + private static final Writable MINECRAFT_FONT_IMAGE_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/defaults/minecraft_font.png"); + public static final LanguageGlyphCollection LANGUAGE_GLYPH = minecraftFontGlyphCollection( + MINECRAFT_FONT_KEY, + Key.key(MINECRAFT_FONT_KEY.asString().concat(".png")), + List.of(new TextureProperties(12,-6), + new TextureProperties(8,-24), + new TextureProperties(8,-36))); + + private GlyphFont() {} + + public static Collection compile() { + return GlyphCompiler.instance().compile(SPACES, LANGUAGE_GLYPH); + } + + private static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(@NotNull Key fontKey, @NotNull Key textureKey, @NotNull List<@NotNull TextureProperties> propertiesList) { + return LanguageGlyphCollection.of(fontKey, + Texture.texture(textureKey, MINECRAFT_FONT_IMAGE_WRITABLE), + propertiesList, + List.of(" АБВГДЕЖЗИК", + "ЛМНОПРСТУФХЦЧШЩЪ", + "ЫЬЭЮЯабвгдежзикл", + "мнопрстуфхцчшщъы", + "ьэюяйЙёЁ ", + "₽!\"#$%&'()*+,-./", + "0123456789: <=>?", + "@ABCDEFGHIJKLMNO", + "PQRSTUVWXYZ[\\]^_", + "`abcdefghijklmno", + "pqrstuvwxyz{|} ") + ); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/AppendableGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/AppendableGlyph.java new file mode 100644 index 0000000..575546b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/AppendableGlyph.java @@ -0,0 +1,3 @@ +package ru.dragonestia.msb3.api.glyph.glyph; + +public interface AppendableGlyph extends Glyph {} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/EmptyGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/EmptyGlyph.java new file mode 100644 index 0000000..55b7f45 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/EmptyGlyph.java @@ -0,0 +1,24 @@ +package ru.dragonestia.msb3.api.glyph.glyph; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EmptyGlyph implements AppendableGlyph { + + public static final EmptyGlyph INSTANCE = new EmptyGlyph(); + + @Override + public @NotNull Component toAdventure() throws ResourceNotProducedException { + return Component.text(""); + } + + @Override + public int width() { + return 0; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/Glyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/Glyph.java new file mode 100644 index 0000000..0241f27 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/Glyph.java @@ -0,0 +1,21 @@ +package ru.dragonestia.msb3.api.glyph.glyph; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; + +public interface Glyph { + + String DEFAULT_NAMESPACE = "glyphs"; + + Key DEFAULT_FONT_KEY = Key.key(DEFAULT_NAMESPACE, "default"); + + Key DEFAULT_SPACES_FONT_KEY = Key.key(DEFAULT_NAMESPACE, "spaces"); + + int SEPARATOR_WIDTH = 1; + + @NotNull Component toAdventure() throws ResourceNotProducedException; + + int width(); +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilder.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilder.java new file mode 100644 index 0000000..16d9e96 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilder.java @@ -0,0 +1,60 @@ +package ru.dragonestia.msb3.api.glyph.glyph; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; + +import java.util.List; + +public interface GlyphComponentBuilder { + + static @NotNull GlyphComponentBuilder universal(SpacesGlyphResourceProducer spacesProducer) { + return new GlyphComponentBuilderImpl(spacesProducer, 0, Component.text("")); + } + + static @NotNull GlyphComponentBuilder gui(SpacesGlyphResourceProducer spacesProducer) { + return new GlyphComponentBuilderImpl(spacesProducer, -8, Component.text("", NamedTextColor.WHITE)); + } + + @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull AppendableGlyph glyph); + + default @NotNull GlyphComponentBuilder append(PositionType positionType, @NotNull AppendableGlyph glyph) { + return append(positionType, 0, glyph); + } + + @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull List glyphList); + + default @NotNull GlyphComponentBuilder append(PositionType positionType, @NotNull List glyphList) { + return append(positionType, 0, glyphList); + } + + // Append with default position type + + default @NotNull GlyphComponentBuilder append(int position, @NotNull AppendableGlyph glyph) { + return append(PositionType.ABSOLUTE, position, glyph); + } + + default @NotNull GlyphComponentBuilder append(@NotNull AppendableGlyph glyph) { + return append(PositionType.ABSOLUTE, glyph); + } + + default @NotNull GlyphComponentBuilder append(int position, @NotNull List glyphList) { + return append(PositionType.ABSOLUTE, position, glyphList); + } + + default @NotNull GlyphComponentBuilder append(@NotNull List glyphList) { + return append(PositionType.ABSOLUTE, glyphList); + } + + @NotNull Component build(boolean keepInitialPosition); + + default @NotNull Component build() { + return build(true); + } + + enum PositionType { + ABSOLUTE, + RELATIVE + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilderImpl.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilderImpl.java new file mode 100644 index 0000000..ffc0815 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/GlyphComponentBuilderImpl.java @@ -0,0 +1,86 @@ +package ru.dragonestia.msb3.api.glyph.glyph; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; + +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class GlyphComponentBuilderImpl implements GlyphComponentBuilder { + + private final SpacesGlyphResourceProducer spacesProducer; + private final int initialPosition; + private final Component baseComponent; + + private final List glyphs = new ArrayList<>(); + + private int previousElementsWidth; + + @Override + public @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull AppendableGlyph glyph) { + if (positionType == PositionType.ABSOLUTE && previousElementsWidth != 0) { + glyphs.add(spacesProducer.translate((-1) * previousElementsWidth)); + previousElementsWidth = 0; + } + + if (position != 0) { + glyphs.add(spacesProducer.translate(position)); + } + + glyphs.add(glyph); + this.previousElementsWidth += position + glyph.width(); + + return this; + } + + @Override + public @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull List glyphList) { + if (positionType == PositionType.ABSOLUTE && previousElementsWidth != 0) { + glyphs.add(spacesProducer.translate((-1) * previousElementsWidth)); + previousElementsWidth = 0; + } + + if (position != 0) { + glyphs.add(spacesProducer.translate(position)); + } + + int width = 0; + for (AppendableGlyph glyph : glyphList) { + glyphs.add(glyph); + width += glyph.width(); + } + + this.previousElementsWidth += position + width; + + return this; + } + + @Override + public @NotNull Component build(boolean keepInitialPosition) { + if (keepInitialPosition) { + previousElementsWidth += initialPosition; + + // Component should have zero width finally + if (previousElementsWidth != 0) { + glyphs.add(spacesProducer.translate((-1) * previousElementsWidth)); + } + } + + var component = baseComponent; + + if (initialPosition != 0) { + component = component.append(spacesProducer.translate(initialPosition).toAdventure()); + } + + for (Glyph glyph : glyphs) { + component = component.append(glyph.toAdventure()); + } + + return component; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceAlreadyProducedException.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceAlreadyProducedException.java new file mode 100644 index 0000000..3fd0416 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceAlreadyProducedException.java @@ -0,0 +1,3 @@ +package ru.dragonestia.msb3.api.glyph.glyph.exception; + +public class ResourceAlreadyProducedException extends RuntimeException {} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceNotProducedException.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceNotProducedException.java new file mode 100644 index 0000000..8a66085 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/exception/ResourceNotProducedException.java @@ -0,0 +1,3 @@ +package ru.dragonestia.msb3.api.glyph.glyph.exception; + +public class ResourceNotProducedException extends RuntimeException {} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyph.java new file mode 100644 index 0000000..6f071f4 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyph.java @@ -0,0 +1,33 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import team.unnamed.creative.texture.Texture; + +public interface ImageGlyph extends AppendableGlyph, ResourceProducer { + + static @NotNull ImageGlyph of(@NotNull Key key, + @NotNull Texture texture, + @NotNull TextureProperties properties) { + return new ImageGlyphImpl(key, texture, properties); + } + + @Deprecated(forRemoval = true) + static @NotNull ImageGlyph of(@NotNull Texture texture, + @NotNull TextureProperties properties) { + return of(Glyph.DEFAULT_FONT_KEY, texture, properties); + } + + @NotNull Character character() throws ResourceNotProducedException; + + @NotNull Texture texture(); + + default @NotNull Component toAdventure() throws ResourceNotProducedException { + return Component.text(character()).font(fontKey()); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyphImpl.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyphImpl.java new file mode 100644 index 0000000..fd2bdcc --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/ImageGlyphImpl.java @@ -0,0 +1,105 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import ru.dragonestia.msb3.api.glyph.util.ImageUtil; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.texture.Texture; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class ImageGlyphImpl implements ImageGlyph { + + private final Key fontKey; + private final Texture texture; + private final TextureProperties properties; + + private Character character; + private Set fontProviders; + + private int width = -1; + + @Override + public @NotNull Key fontKey() { + return fontKey; + } + + @Override + public boolean produced() { + return fontProviders != null; + } + + @Override + public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException { + if (fontProviders != null) { + throw new ResourceAlreadyProducedException(); + } + + var fontProviderBuilder = FontProvider.bitMap(); + + this.character = characterFactory.nextCharacter(); + + fontProviderBuilder.characters(String.valueOf(character)); + fontProviderBuilder.file(texture.key()); + fontProviderBuilder.ascent(properties.ascent()); + fontProviderBuilder.height(properties.height()); + + this.fontProviders = Collections.singleton(fontProviderBuilder.build()); + } + + @Override + public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException { + if (fontProviders == null) { + throw new ResourceNotProducedException(); + } + return fontProviders; + } + + @Override + public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException { + return Collections.singleton(texture); + } + + @Override + public int width() { + if (width == -1) { + try { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(texture.data().toByteArray())); + int fileHeight = image.getHeight(); + width = (int) Math.ceil( + ((double) properties.height() / (double) fileHeight) + * ImageUtil.calculateWidth(image)) + Glyph.SEPARATOR_WIDTH; + } catch (IOException e) { + e.printStackTrace(); + } + } + return width; + } + + @Override + public @NotNull Character character() throws ResourceNotProducedException { + if (fontProviders == null) { + throw new ResourceNotProducedException(); + } + return character; + } + + @Override + public @NotNull Texture texture() { + return texture; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/TextureProperties.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/TextureProperties.java new file mode 100644 index 0000000..6489741 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/TextureProperties.java @@ -0,0 +1,3 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image; + +public record TextureProperties(int height, int ascent) {} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/ColorableGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/ColorableGlyph.java new file mode 100644 index 0000000..55b3ddf --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/ColorableGlyph.java @@ -0,0 +1,11 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.Nullable; + +public interface ColorableGlyph { + + @Nullable TextColor color(); + + void updateColor(@Nullable TextColor color); +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java new file mode 100644 index 0000000..3208d92 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollection.java @@ -0,0 +1,42 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import team.unnamed.creative.texture.Texture; + +import java.util.List; + +public interface LanguageGlyphCollection extends ResourceProducer { + + static @NotNull LanguageGlyphCollection of(@NotNull Key fontKey, + @NotNull Texture texture, + @NotNull List<@NotNull TextureProperties> propertiesList, + @NotNull List<@NotNull String> charactersMapping) { + return new LanguageGlyphCollectionImpl(fontKey, texture, propertiesList, charactersMapping); + } + + @Deprecated(forRemoval = true) + static @NotNull LanguageGlyphCollection of(@NotNull Texture texture, + @NotNull List<@NotNull TextureProperties> propertiesList, + @NotNull List<@NotNull String> charactersMapping) { + return of(Glyph.DEFAULT_FONT_KEY, texture, propertiesList, charactersMapping); + } + + @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException; + + @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException; + + default @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character) throws IllegalArgumentException { + return translate(height, ascent, character, null); + } + + default @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text) throws IllegalArgumentException { + return translate(height, ascent, text, null); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java new file mode 100644 index 0000000..36eba45 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/LanguageGlyphCollectionImpl.java @@ -0,0 +1,92 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.texture.Texture; + +import java.util.*; + +public class LanguageGlyphCollectionImpl implements LanguageGlyphCollection { + + private final Key fontKey; + private final Texture texture; + + private final Map propertiesToMulticharacterMap = new HashMap<>(); + + private Set fontProviders; + + LanguageGlyphCollectionImpl( + Key fontKey, + Texture texture, + List propertiesList, + List charactersMapping + ) { + this.fontKey = fontKey; + this.texture = texture; + + for (TextureProperties properties : propertiesList) { + propertiesToMulticharacterMap.put(properties, MulticharacterImageGlyphCollection.of(fontKey, texture, properties, charactersMapping)); + } + } + + + @Override + public @NotNull Key fontKey() { + return fontKey; + } + + @Override + public boolean produced() { + return fontProviders != null; + } + + @Override + public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException { + if (fontProviders != null) { + throw new ResourceAlreadyProducedException(); + } + + fontProviders = new HashSet<>(); + + for (var entry : propertiesToMulticharacterMap.entrySet()) { + entry.getValue().produceResources(characterFactory); + fontProviders.addAll(entry.getValue().fontProviders()); + } + } + + @Override + public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException { + return fontProviders; + } + + @Override + public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException { + return Collections.singleton(texture); + } + + @Override + public @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException { + TextureProperties properties = new TextureProperties(height, ascent); + if (!propertiesToMulticharacterMap.containsKey(properties)) { + throw new IllegalArgumentException(); + } + return propertiesToMulticharacterMap.get(properties).translate(character, color); + } + + @Override + public @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException { + TextureProperties properties = new TextureProperties(height, ascent); + if (!propertiesToMulticharacterMap.containsKey(properties)) { + throw new IllegalArgumentException(); + } + return propertiesToMulticharacterMap.get(properties).translate(text, color); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollection.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollection.java new file mode 100644 index 0000000..8e2f8e2 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollection.java @@ -0,0 +1,54 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyph; +import team.unnamed.creative.texture.Texture; + +import java.util.ArrayList; +import java.util.List; + +public interface MulticharacterImageGlyphCollection extends ResourceProducer { + + static @NotNull MulticharacterImageGlyphCollection of(@NotNull Key fontKey, + @NotNull Texture texture, + @NotNull TextureProperties properties, + @NotNull List<@NotNull String> charactersMapping) { + return new MulticharacterImageGlyphCollectionImpl(fontKey, texture, properties, charactersMapping); + } + + @Deprecated(forRemoval = true) + static @NotNull MulticharacterImageGlyphCollection of(@NotNull Texture texture, + @NotNull TextureProperties properties, + @NotNull List<@NotNull String> charactersMapping) { + return of(Glyph.DEFAULT_FONT_KEY, texture, properties, charactersMapping); + } + + @NotNull AppendableGlyph translate(@NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException; + + default @NotNull List<@NotNull AppendableGlyph> translate(@NotNull String text, @Nullable TextColor color) throws IllegalArgumentException { + List glyphs = new ArrayList<>(); + for (char character : text.toCharArray()) { + if (character == ' ') { + glyphs.add(SpacesGlyph.DEFAULT_SPACE_GLYPH); + } else { + glyphs.add(translate(character, color)); + } + } + return glyphs; + } + + default @NotNull AppendableGlyph translate(@NotNull Character character) throws IllegalArgumentException { + return translate(character, null); + } + + default @NotNull List<@NotNull AppendableGlyph> translate(@NotNull String text) throws IllegalArgumentException { + return translate(text, null); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollectionImpl.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollectionImpl.java new file mode 100644 index 0000000..b7879b4 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/MulticharacterImageGlyphCollectionImpl.java @@ -0,0 +1,131 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import ru.dragonestia.msb3.api.glyph.util.ImageUtil; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.texture.Texture; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.*; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class MulticharacterImageGlyphCollectionImpl implements MulticharacterImageGlyphCollection { + + private final Key fontKey; + private final Texture texture; + private final TextureProperties properties; + private final List charactersMapping; + + private final Map originToArbitraryCharacterMap = new HashMap<>(); + + private Set fontProviders; + + private BufferedImage image; + + @Override + public @NotNull Key fontKey() { + return fontKey; + } + + @Override + public boolean produced() { + return fontProviders != null; + } + + @Override + public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException { + if (fontProviders != null) { + throw new ResourceAlreadyProducedException(); + } + + var fontProviderBuilder = FontProvider.bitMap(); + fontProviderBuilder.file(texture.key()); + fontProviderBuilder.ascent(properties.ascent()); + fontProviderBuilder.height(properties.height()); + + List mappingLines = new ArrayList<>(); + + for (String mappingLine : charactersMapping) { + StringBuilder builder = new StringBuilder(); + for (char character : mappingLine.toCharArray()) { + char arbitraryCharacter = characterFactory.nextCharacter(); + originToArbitraryCharacterMap.put(character, arbitraryCharacter); + builder.append(arbitraryCharacter); + } + mappingLines.add(builder.toString()); + } + + fontProviderBuilder.characters(mappingLines); + + this.fontProviders = Collections.singleton(fontProviderBuilder.build()); + } + + @Override + public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException { + return fontProviders; + } + + @Override + public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException { + return Collections.singleton(texture); + } + + @Override + public @NotNull PreparedImageGlyph translate(@NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException { + if (!originToArbitraryCharacterMap.containsKey(character)) { + throw new IllegalArgumentException(); + } + + int width = 0; + for (int lineIndex = 0; lineIndex < charactersMapping.size(); lineIndex++) { + String line = charactersMapping.get(lineIndex); + for (int characterIndex = 0; characterIndex < line.toCharArray().length; characterIndex++) { + if (line.charAt(characterIndex) == character) { + if (image == null) { + cacheImage(); + } + + if (image == null) { + throw new IllegalArgumentException(); + } + + int filePartWidth = image.getWidth() / charactersMapping.get(0).length(); + int filePartHeight = image.getHeight() / charactersMapping.size(); + + width = (int) Math.ceil( + ((double) properties.height() / (double) filePartHeight) + * ImageUtil.calculateWidth( + image, filePartWidth * characterIndex, filePartHeight * lineIndex, + filePartWidth * (characterIndex + 1), filePartHeight * (lineIndex + 1) + )) + Glyph.SEPARATOR_WIDTH; + break; + } + } + } + + return new PreparedImageGlyph(fontKey, originToArbitraryCharacterMap.get(character), width, color); + } + + private void cacheImage() { + try { + image = ImageIO.read(new ByteArrayInputStream(texture.data().toByteArray())); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/PreparedImageGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/PreparedImageGlyph.java new file mode 100644 index 0000000..c8d2d89 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/image/multicharacter/PreparedImageGlyph.java @@ -0,0 +1,43 @@ +package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; + +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class PreparedImageGlyph implements AppendableGlyph, ColorableGlyph { + + private final Key key; + private final Character character; + private final int width; + private @Nullable TextColor color; + + @Override + public @NotNull Component toAdventure() { + return Component.text(character) + .font(key) + .color(color == null ? NamedTextColor.BLACK : color); + } + + @Override + public int width() { + return width; + } + + @Override + public @Nullable TextColor color() { + return color; + } + + @Override + public void updateColor(@Nullable TextColor color) { + this.color = color; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/AbstractSpacesGlyphResourceProducer.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/AbstractSpacesGlyphResourceProducer.java new file mode 100644 index 0000000..1c42e8e --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/AbstractSpacesGlyphResourceProducer.java @@ -0,0 +1,64 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; + +import lombok.RequiredArgsConstructor; +import ru.dragonestia.msb3.api.glyph.glyph.EmptyGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import ru.dragonestia.msb3.api.glyph.util.ArrayUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +public abstract class AbstractSpacesGlyphResourceProducer implements SpacesGlyphResourceProducer { + + private final Key fontKey; + + protected Map mapping; + + @Override + public @NotNull Key fontKey() { + return fontKey; + } + + @Override + public boolean produced() { + return mapping != null; + } + + @Override + public Glyph translate(int length) throws ResourceNotProducedException { + if (mapping == null) { + throw new ResourceNotProducedException(); + } + + if (length == 0) { + return EmptyGlyph.INSTANCE; + } + + int sign = length > 0 ? 1 : -1; + String binaryString = Integer.toBinaryString(Math.abs(length)); + + List characters = new ArrayList<>(); + + int currentRankLength = 1; + for (int index = 0; index < binaryString.length(); index++) { + char digit = binaryString.charAt(binaryString.length() - index - 1); + if (digit == '1') { + int partLength = currentRankLength * sign; + if (!mapping.containsKey(partLength)) { + throw new IllegalArgumentException("Too much length"); + } + characters.add(mapping.get(partLength)); + } + currentRankLength *= 2; + } + + return new SpacesGlyph(fontKey(), ArrayUtil.toCharArray(characters), length); + } + +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpaceGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpaceGlyph.java new file mode 100644 index 0000000..4d38585 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpaceGlyph.java @@ -0,0 +1,24 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import team.unnamed.creative.font.Font; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +public class DefaultSpaceGlyph implements AppendableGlyph { + + @Override + public @NotNull Component toAdventure() throws ResourceNotProducedException { + return Component.text(" ").font(Font.MINECRAFT_DEFAULT); + } + + @Override + public int width() { + return 4; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpacesGlyphResourceProducer.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpacesGlyphResourceProducer.java new file mode 100644 index 0000000..27fb036 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/DefaultSpacesGlyphResourceProducer.java @@ -0,0 +1,91 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import team.unnamed.creative.base.Writable; +import team.unnamed.creative.font.BitMapFontProvider; +import team.unnamed.creative.font.FontProvider; +import team.unnamed.creative.texture.Texture; + +import java.util.*; + +public class DefaultSpacesGlyphResourceProducer extends AbstractSpacesGlyphResourceProducer { + + private final Key textureKey; + private final Writable writable; + + private Set textures; + private Set fontProviders; + + public DefaultSpacesGlyphResourceProducer(Key fontKey, Key textureKey, Writable writable) { + super(fontKey); + this.textureKey = textureKey; + this.writable = writable; + } + + @Override + public boolean produced() { + return textures != null; + } + + @Override + public void produceResources(ArbitraryCharacterFactory characterFactory) { + if (textures != null) { + throw new ResourceAlreadyProducedException(); + } + + this.mapping = new HashMap<>(); + + Set fontProviders = new HashSet<>(); + + for (int length = 1; length <= 2048; length *= 2) { + fontProviders.add(prepareBuilder(characterFactory, length).build()); + fontProviders.add(prepareBuilder(characterFactory, length * (-1)).build()); + } + + this.textures = Collections.singleton(Texture.texture(textureKey, writable)); + this.fontProviders = fontProviders; + } + + @Override + public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException { + if (fontProviders == null) { + throw new ResourceNotProducedException(); + } + return fontProviders; + } + + @Override + public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException { + if (textures == null) { + throw new ResourceNotProducedException(); + } + return textures; + } + + @NotNull + private BitMapFontProvider.Builder prepareBuilder(ArbitraryCharacterFactory characterFactory, int length) { + var fontProviderBuilder = FontProvider.bitMap(); + + char character = characterFactory.nextCharacter(); + + fontProviderBuilder.characters(String.valueOf(character)); + fontProviderBuilder.file(textureKey); + + if (length > 0) { + fontProviderBuilder.height(length - 1); + fontProviderBuilder.ascent(0); + } else { + fontProviderBuilder.height(length - 2); + fontProviderBuilder.ascent(-32768); + } + + mapping.put(length, character); + + return fontProviderBuilder; + } + +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyph.java new file mode 100644 index 0000000..5c9fa41 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyph.java @@ -0,0 +1,40 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import team.unnamed.creative.base.Writable; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class SpacesGlyph implements Glyph { + + private static final Key DEFAULT_SPACE_TEXTURE_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "space"); + public static final @NotNull DefaultSpaceGlyph DEFAULT_SPACE_GLYPH = new DefaultSpaceGlyph(); + private final Key key; + private final char[] characters; + private final int length; + + public static @NotNull SpacesGlyphResourceProducer create(@NotNull Key fontKey, + @NotNull Key textureKey, + @NotNull Writable spacesWritable) { + return new DefaultSpacesGlyphResourceProducer(fontKey, textureKey, spacesWritable); + } + + public static SpacesGlyphResourceProducer create(@NotNull Writable spacesWritable) { + return create(Glyph.DEFAULT_SPACES_FONT_KEY, DEFAULT_SPACE_TEXTURE_KEY, spacesWritable); + } + + @Override + public @NotNull Component toAdventure() { + return Component.text(new String(characters)).font(key); + } + + @Override + public int width() { + return length; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyphResourceProducer.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyphResourceProducer.java new file mode 100644 index 0000000..9b8ca87 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/SpacesGlyphResourceProducer.java @@ -0,0 +1,10 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space; + +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; + +public interface SpacesGlyphResourceProducer extends ResourceProducer { + + Glyph translate(int length) throws ResourceNotProducedException; +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyph.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyph.java new file mode 100644 index 0000000..1901886 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyph.java @@ -0,0 +1,18 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space.mojang; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; + +public interface MojangSpacesGlyph extends Glyph, ResourceProducer { + + static @NotNull SpacesGlyphResourceProducer create(@NotNull Key key) { + return new MojangSpacesGlyphResourceProducer(key); + } + + static SpacesGlyphResourceProducer create() { + return create(Glyph.DEFAULT_SPACES_FONT_KEY); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyphResourceProducer.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyphResourceProducer.java new file mode 100644 index 0000000..eccef64 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/glyph/space/mojang/MojangSpacesGlyphResourceProducer.java @@ -0,0 +1,60 @@ +package ru.dragonestia.msb3.api.glyph.glyph.space.mojang; + +import net.kyori.adventure.key.Key; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException; +import ru.dragonestia.msb3.api.glyph.glyph.space.AbstractSpacesGlyphResourceProducer; +import team.unnamed.creative.font.FontProvider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; + +public class MojangSpacesGlyphResourceProducer extends AbstractSpacesGlyphResourceProducer { + + private Set fontProviders; + + public MojangSpacesGlyphResourceProducer(Key key) { + super(key); + } + + @Override + public boolean produced() { + return fontProviders != null; + } + + @Override + public void produceResources(ArbitraryCharacterFactory characterFactory) { + if (fontProviders != null) { + throw new ResourceAlreadyProducedException(); + } + + mapping = new HashMap<>(); + + var fontProviderBuilder = FontProvider.space(); + + for (int length = 1; length <= 2048; length *= 2) { + fontProviderBuilder.advance(retrieveCharacter(characterFactory, length), length); + fontProviderBuilder.advance(retrieveCharacter(characterFactory, length * (-1)), length * (-1)); + } + + this.fontProviders = Collections.singleton(fontProviderBuilder.build()); + } + + @Override + public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException { + if (fontProviders == null) { + throw new ResourceNotProducedException(); + } + return fontProviders; + } + + private Character retrieveCharacter(ArbitraryCharacterFactory characterFactory, int length) { + char character = characterFactory.nextCharacter(); + mapping.put(length, character); + return character; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/DefaultGlyphResourcePack.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/DefaultGlyphResourcePack.java new file mode 100644 index 0000000..7cf3141 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/DefaultGlyphResourcePack.java @@ -0,0 +1,63 @@ +package ru.dragonestia.msb3.api.glyph.pack; + +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import team.unnamed.creative.part.ResourcePackPart; + +import java.util.*; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class DefaultGlyphResourcePack implements GlyphResourcePack { + + private final Map raw = new HashMap<>(); + private final Map compiled = new HashMap<>(); + + private final Set resources = new HashSet<>(); + + @Override + public @NotNull Collection<@NotNull ResourcePackPart> all() { + if (!raw.isEmpty()) { + compileAll(); + } + return resources; + } + + @Override + public void compileAll() { + var resources = GlyphCompiler.instance().compile(raw.values()); + this.resources.addAll(resources); + compiled.putAll(raw); + raw.clear(); + } + + @Override + public @NotNull GlyphResourcePack with(@NotNull ResourceIdentifier<@NotNull T> id, @NotNull T producer) { + if (raw.containsKey(id.key()) || compiled.containsKey(id.key())) { + throw new IllegalArgumentException("Producer with this identifier already registered"); + } + raw.put(id.key(), producer); + return this; + } + + @Override + public @NotNull GlyphResourcePack with(@NotNull ResourcePackPart resource) { + resources.add(resource); + return this; + } + + @Override + public @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException { + if (!compiled.containsKey(id.key())) { + throw new IllegalArgumentException("Producer with that identifier is not compiled"); + } + ResourceProducer producer = compiled.get(id.key()); + if (!id.getType().isAssignableFrom(producer.getClass())) { + throw new IllegalArgumentException("Wrong producer type"); + } + return (T) producer; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/GlyphResourcePack.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/GlyphResourcePack.java new file mode 100644 index 0000000..fd1877f --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/GlyphResourcePack.java @@ -0,0 +1,58 @@ +package ru.dragonestia.msb3.api.glyph.pack; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.space.mojang.MojangSpacesGlyph; +import team.unnamed.creative.ResourcePack; +import team.unnamed.creative.part.ResourcePackPart; + +import java.util.Collection; + +public interface GlyphResourcePack { + + @NotNull Collection<@NotNull ResourcePackPart> all(); + + void compileAll(); + + @Contract("_, _ -> this") + @NotNull GlyphResourcePack with(@NotNull ResourceIdentifier<@NotNull T> id, @NotNull T producer); + + @Contract("_ -> this") + @NotNull GlyphResourcePack with(@NotNull ResourcePackPart resource); + + @Contract("_ -> this") + default @NotNull GlyphResourcePack with(@NotNull ResourcePackPart... resources) { + for (ResourcePackPart resource : resources) { + with(resource); + } + return this; + } + + @Contract("_ -> this") + default @NotNull GlyphResourcePack with(@NotNull Collection<@NotNull ResourcePackPart> resources) { + resources.forEach(this::with); + return this; + } + + @Contract("-> this") + default @NotNull GlyphResourcePack withMojangSpaces() { + with(ResourceIdentifier.SPACES, MojangSpacesGlyph.create()); + return this; + } + + @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException; + + default @NotNull SpacesGlyphResourceProducer spaces() { + return get(ResourceIdentifier.SPACES); + } + + default void writeAll(@NotNull ResourcePack resourcePack) { + all().forEach(resourcePack::part); + } + + static @NotNull GlyphResourcePack create() { + return new DefaultGlyphResourcePack(); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ImageResourceIdentifier.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ImageResourceIdentifier.java new file mode 100644 index 0000000..bf6a472 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ImageResourceIdentifier.java @@ -0,0 +1,12 @@ +package ru.dragonestia.msb3.api.glyph.pack; + +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph; + +public interface ImageResourceIdentifier extends ResourceIdentifier<@NotNull ImageGlyph> { + + @Override + default @NotNull Class<@NotNull ImageGlyph> getType() { + return ImageGlyph.class; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ResourceIdentifier.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ResourceIdentifier.java new file mode 100644 index 0000000..0d8885b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/ResourceIdentifier.java @@ -0,0 +1,19 @@ +package ru.dragonestia.msb3.api.glyph.pack; + +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection; +import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer; + +public interface ResourceIdentifier { + + @NotNull StringIdentifier<@NotNull SpacesGlyphResourceProducer> SPACES = StringIdentifier.of("spaces", SpacesGlyphResourceProducer.class); + + @NotNull StringIdentifier<@NotNull LanguageGlyphCollection> MINECRAFT_FONT = StringIdentifier.of("minecraft_font", LanguageGlyphCollection.class); + + String STRING_IDENTIFIER_NAMESPACE = "glyphs"; + + @NotNull String key(); + + @NotNull Class getType(); +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/StringIdentifier.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/StringIdentifier.java new file mode 100644 index 0000000..52e8621 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/pack/StringIdentifier.java @@ -0,0 +1,37 @@ +package ru.dragonestia.msb3.api.glyph.pack; + +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer; +import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph; + +public class StringIdentifier implements ResourceIdentifier { + + private final @NotNull String id; + + private final @NotNull Class type; + + protected StringIdentifier(@NotNull String id, @NotNull Class type) { + this.id = id; + this.type = type; + } + + @Override + public @NotNull Class getType() { + return type; + } + + @Override + public @NotNull String key() { + return id; + } + + public static @NotNull StringIdentifier<@NotNull T> of( + @NotNull String id, + @NotNull Class type) { + return new StringIdentifier<>(id, type); + } + + public static @NotNull StringIdentifier<@NotNull ImageGlyph> image(@NotNull String id) { + return new StringIdentifier<>(id, ImageGlyph.class); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ArrayUtil.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ArrayUtil.java new file mode 100644 index 0000000..f7029b5 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ArrayUtil.java @@ -0,0 +1,19 @@ +package ru.dragonestia.msb3.api.glyph.util; + +import org.jetbrains.annotations.NotNull; + +import lombok.experimental.UtilityClass; + +import java.util.List; + +@UtilityClass +public class ArrayUtil { + + public char[] toCharArray(@NotNull List list) { + char[] arr = new char[list.size()]; + for (int i = 0; i < list.size(); i++) { + arr[i] = list.get(i); + } + return arr; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ImageUtil.java b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ImageUtil.java new file mode 100644 index 0000000..1bf4c28 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/glyph/util/ImageUtil.java @@ -0,0 +1,28 @@ +package ru.dragonestia.msb3.api.glyph.util; + +import lombok.experimental.UtilityClass; + +import java.awt.*; +import java.awt.image.BufferedImage; + +@UtilityClass +public class ImageUtil { + + public int calculateWidth(BufferedImage image, int xFrom, int yFrom, int xTo, int yTo) { + int width; + for (width = xTo - 1; width > xFrom; width--) { + for (int height = yFrom; height < yTo; height++) { + if (new Color(image.getRGB(width, height), true) + .getAlpha() == 255) { + return width - xFrom + 1; + } + } + } + + return width - xFrom + 1; + } + + public int calculateWidth(BufferedImage image) { + return calculateWidth(image, 0, 0, image.getWidth(), image.getHeight()); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/item/BlankSlotItem.java b/api/src/main/java/ru/dragonestia/msb3/api/item/BlankSlotItem.java new file mode 100644 index 0000000..8d4ded5 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/item/BlankSlotItem.java @@ -0,0 +1,58 @@ +package ru.dragonestia.msb3.api.item; + +import net.kyori.adventure.key.Key; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import ru.dragonestia.msb3.api.ServerBootstrap; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.util.ResourceFromJar; +import team.unnamed.creative.base.Writable; +import team.unnamed.creative.model.*; +import team.unnamed.creative.part.ResourcePackPart; +import team.unnamed.creative.texture.Texture; + +import java.util.Arrays; +import java.util.Collection; + +public class BlankSlotItem { + + private static final Writable BLANK_SLOT_IMAGE_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/defaults/blank_slot.png"); + private static ItemStack stack; + + private BlankSlotItem() {} + + public synchronized static ItemStack getItem() { + if (stack == null) { + stack = ItemStack.builder(Material.PAPER) + .customModelData(1) + .build(); + } + + return stack; + } + + public static Collection compile() { + var modelKey = Key.key(Glyph.DEFAULT_NAMESPACE, "blank_slot"); + var itemKey = Key.key("item/paper"); + + Model blankSlotModel = Model.model() + .key(modelKey) + .parent(Model.ITEM_GENERATED) + .textures(ModelTextures.builder() + .layers(ModelTexture.ofKey(modelKey)) + .build()) + .build(); + + Model paperItemModel = Model.model() + .key(itemKey) + .parent(Model.ITEM_GENERATED) + .textures(ModelTextures.builder() + .layers(ModelTexture.ofKey(itemKey)) + .build()) + .overrides(ItemOverride.of(modelKey, ItemPredicate.customModelData(1))) + .build(); + + Texture texture = Texture.texture(Key.key("glyphs", "blank_slot.png"), BLANK_SLOT_IMAGE_WRITABLE); + return Arrays.asList(blankSlotModel, paperItemModel, texture); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java b/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java index 009d2dc..bc5ae9f 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/resource/ResourcePackManager.java @@ -2,26 +2,79 @@ package ru.dragonestia.msb3.api.resource; import lombok.Getter; import lombok.extern.log4j.Log4j2; +import net.kyori.adventure.key.Key; +import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler; +import ru.dragonestia.msb3.api.glyph.font.GlyphFont; +import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack; +import ru.dragonestia.msb3.api.item.BlankSlotItem; +import ru.dragonestia.msb3.api.title.BlackScreen; import team.unnamed.creative.ResourcePack; +import team.unnamed.creative.atlas.Atlas; +import team.unnamed.creative.atlas.AtlasSource; import team.unnamed.creative.base.Writable; +import team.unnamed.creative.model.Model; import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter; import java.io.File; +import java.util.HashSet; +import java.util.Set; +@Getter @Log4j2 public class ResourcePackManager { - @Getter private final ResourcePack resourcePack; + private final ResourcePack resourcePack; + private final GlyphResourcePack glyphResourcePack; public ResourcePackManager() { resourcePack = ResourcePack.resourcePack(); resourcePack.packMeta(34, "Dragonestia MSB3 - Resource pack"); resourcePack.icon(Writable.resource(getClass().getClassLoader(), "logo.png")); resourcePack.unknownFile("credits.txt", Writable.stringUtf8("dragonestia.ru")); + + glyphResourcePack = GlyphResourcePack.create(); + initDefaultGlyphs(); + } + + private void initDefaultGlyphs() { + glyphResourcePack.with(BlankSlotItem.compile()); + glyphResourcePack.with(GlyphCompiler.instance().compile(BlackScreen.GLYPH)); + glyphResourcePack.with(GlyphFont.compile()); } public void compile() { + glyphResourcePack.writeAll(resourcePack); + generateAtlases(); MinecraftResourcePackWriter.minecraft().writeToZipFile(new File("resource-pack.zip"), resourcePack); log.info("Compiled resource pack. File: ./resource-pack.zip"); } + + private void generateAtlases() { + Atlas.Builder atlasBuilder = Atlas.atlas(); + atlasBuilder.key(Key.key("minecraft", "blocks")); + Set applicableTextureKeys = new HashSet<>(); + + for (Model model: resourcePack.models()) { + for (var layer: model.textures().layers()) { + var key = layer.key(); + if (key != null) { + applicableTextureKeys.add(key.asString().concat(".png")); + } + } + + for (var texture: model.textures().variables().values()) { + var key = texture.key(); + if (key != null) { + applicableTextureKeys.add(key.asString().concat(".png")); + } + } + } + + var sources = resourcePack.textures() + .stream() + //.filter(texture -> !applicableTextureKeys.contains(texture.key().asString())) + .map(texture -> (AtlasSource) AtlasSource.single(Key.key(texture.key().toString().replace(".png", "")))).toList(); + atlasBuilder.sources(sources); + resourcePack.atlas(atlasBuilder.build()); + } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/title/BlackScreen.java b/api/src/main/java/ru/dragonestia/msb3/api/title/BlackScreen.java new file mode 100644 index 0000000..9852b75 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/title/BlackScreen.java @@ -0,0 +1,35 @@ +package ru.dragonestia.msb3.api.title; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import net.minestom.server.entity.Player; +import ru.dragonestia.msb3.api.ServerBootstrap; +import ru.dragonestia.msb3.api.glyph.glyph.Glyph; +import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph; +import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties; +import ru.dragonestia.msb3.api.util.ResourceFromJar; +import team.unnamed.creative.base.Writable; +import team.unnamed.creative.texture.Texture; + +public class BlackScreen { + + private static final Writable BACKGROUND_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/defaults/fullscreen_background.png"); + private static final Key FULLSCREEN_BACKGROUND_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "fullscreen_background.png"); + public static final ImageGlyph GLYPH = fullscreenBackgroundGlyph(); + + private BlackScreen() {} + + public static void show(Player player) { + player.showTitle(Title.title( + GLYPH.toAdventure(), + Component.empty() + )); + } + + private static ImageGlyph fullscreenBackgroundGlyph() { + return ImageGlyph.of(FULLSCREEN_BACKGROUND_KEY, + Texture.texture(FULLSCREEN_BACKGROUND_KEY, BACKGROUND_WRITABLE), + new TextureProperties(2500, 256)); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java b/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java new file mode 100644 index 0000000..204829d --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/util/ResourceFromJar.java @@ -0,0 +1,28 @@ +package ru.dragonestia.msb3.api.util; + +import team.unnamed.creative.base.Writable; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; + +public interface ResourceFromJar { + + static Writable of(ClassLoader classLoader, String fileName) { + return Writable.inputStream(() -> { + try { + URL url = classLoader.getResource(fileName); + + if (url == null) { + return null; + } + + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } + }); + } +} diff --git a/api/src/main/resources/glyphs/defaults/blank_slot.png b/api/src/main/resources/glyphs/defaults/blank_slot.png new file mode 100644 index 0000000..0058d7a Binary files /dev/null and b/api/src/main/resources/glyphs/defaults/blank_slot.png differ diff --git a/api/src/main/resources/glyphs/defaults/fullscreen_background.png b/api/src/main/resources/glyphs/defaults/fullscreen_background.png new file mode 100644 index 0000000..ae32d15 Binary files /dev/null and b/api/src/main/resources/glyphs/defaults/fullscreen_background.png differ diff --git a/api/src/main/resources/glyphs/defaults/minecraft_font.png b/api/src/main/resources/glyphs/defaults/minecraft_font.png new file mode 100644 index 0000000..5d94bcf Binary files /dev/null and b/api/src/main/resources/glyphs/defaults/minecraft_font.png differ diff --git a/api/src/main/resources/glyphs/defaults/space.png b/api/src/main/resources/glyphs/defaults/space.png new file mode 100644 index 0000000..d59960a Binary files /dev/null and b/api/src/main/resources/glyphs/defaults/space.png differ