feat: integrated Glyphs library

This commit is contained in:
Andrey Terentev 2024-10-21 17:48:22 +07:00
parent fadc170c62
commit e8c588b2b6
46 changed files with 1646 additions and 1 deletions

View File

@ -10,6 +10,8 @@ import java.net.InetSocketAddress;
@Log4j2 @Log4j2
public class ServerBootstrap { public class ServerBootstrap {
public static final ClassLoader CLASS_LOADER = ServerBootstrap.class.getClassLoader();
private static final String[] LOGO = { private static final String[] LOGO = {
"==============================================", "==============================================",
"Minestom Server Base v3 - by ScarletRedMan", "Minestom Server Base v3 - by ScarletRedMan",

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
package ru.dragonestia.msb3.api.glyph.compile;
import org.jetbrains.annotations.NotNull;
public interface ArbitraryCharacterFactory {
@NotNull Character nextCharacter() throws IllegalStateException;
}

View File

@ -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<Character> 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);
}
}

View File

@ -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<ResourcePackPart> fileResources = new HashSet<>();
Set<Key> fontKeys = producers
.stream()
.map(ResourceProducer::fontKey)
.collect(Collectors.toUnmodifiableSet());
for (Key key : fontKeys) {
List<FontProvider> 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;
}
<T> Iterable<T> toIterable(final Stream<T> stream) {
return stream::iterator;
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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<ResourcePackPart> 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{|} ")
);
}
}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.msb3.api.glyph.glyph;
public interface AppendableGlyph extends Glyph {}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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<? extends @NotNull AppendableGlyph> glyphList);
default @NotNull GlyphComponentBuilder append(PositionType positionType, @NotNull List<? extends @NotNull AppendableGlyph> 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<? extends @NotNull AppendableGlyph> glyphList) {
return append(PositionType.ABSOLUTE, position, glyphList);
}
default @NotNull GlyphComponentBuilder append(@NotNull List<? extends @NotNull AppendableGlyph> glyphList) {
return append(PositionType.ABSOLUTE, glyphList);
}
@NotNull Component build(boolean keepInitialPosition);
default @NotNull Component build() {
return build(true);
}
enum PositionType {
ABSOLUTE,
RELATIVE
}
}

View File

@ -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<Glyph> 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<? extends @NotNull AppendableGlyph> 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;
}
}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.msb3.api.glyph.glyph.exception;
public class ResourceAlreadyProducedException extends RuntimeException {}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.msb3.api.glyph.glyph.exception;
public class ResourceNotProducedException extends RuntimeException {}

View File

@ -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());
}
}

View File

@ -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<FontProvider> 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;
}
}

View File

@ -0,0 +1,3 @@
package ru.dragonestia.msb3.api.glyph.glyph.image;
public record TextureProperties(int height, int ascent) {}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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<TextureProperties, MulticharacterImageGlyphCollection> propertiesToMulticharacterMap = new HashMap<>();
private Set<FontProvider> fontProviders;
LanguageGlyphCollectionImpl(
Key fontKey,
Texture texture,
List<TextureProperties> propertiesList,
List<String> 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);
}
}

View File

@ -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<AppendableGlyph> 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);
}
}

View File

@ -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<String> charactersMapping;
private final Map<Character, Character> originToArbitraryCharacterMap = new HashMap<>();
private Set<FontProvider> 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<String> 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();
}
}
}

View File

@ -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;
}
}

View File

@ -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<Integer, Character> 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<Character> 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);
}
}

View File

@ -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;
}
}

View File

@ -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<Texture> textures;
private Set<FontProvider> 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<FontProvider> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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<FontProvider> 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;
}
}

View File

@ -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<String, ResourceProducer> raw = new HashMap<>();
private final Map<String, ResourceProducer> compiled = new HashMap<>();
private final Set<ResourcePackPart> 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 <T extends ResourceProducer> 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 <T extends ResourceProducer> @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;
}
}

View File

@ -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")
<T extends ResourceProducer> @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;
}
<T extends ResourceProducer> @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();
}
}

View File

@ -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;
}
}

View File

@ -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<T extends @NotNull ResourceProducer> {
@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<T> getType();
}

View File

@ -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<T extends @NotNull ResourceProducer> implements ResourceIdentifier<T> {
private final @NotNull String id;
private final @NotNull Class<T> type;
protected StringIdentifier(@NotNull String id, @NotNull Class<T> type) {
this.id = id;
this.type = type;
}
@Override
public @NotNull Class<T> getType() {
return type;
}
@Override
public @NotNull String key() {
return id;
}
public static <T extends @NotNull ResourceProducer> @NotNull StringIdentifier<@NotNull T> of(
@NotNull String id,
@NotNull Class<T> type) {
return new StringIdentifier<>(id, type);
}
public static @NotNull StringIdentifier<@NotNull ImageGlyph> image(@NotNull String id) {
return new StringIdentifier<>(id, ImageGlyph.class);
}
}

View File

@ -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<Character> list) {
char[] arr = new char[list.size()];
for (int i = 0; i < list.size(); i++) {
arr[i] = list.get(i);
}
return arr;
}
}

View File

@ -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());
}
}

View File

@ -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<ResourcePackPart> 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);
}
}

View File

@ -2,26 +2,79 @@ package ru.dragonestia.msb3.api.resource;
import lombok.Getter; import lombok.Getter;
import lombok.extern.log4j.Log4j2; 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.ResourcePack;
import team.unnamed.creative.atlas.Atlas;
import team.unnamed.creative.atlas.AtlasSource;
import team.unnamed.creative.base.Writable; import team.unnamed.creative.base.Writable;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter; import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
import java.io.File; import java.io.File;
import java.util.HashSet;
import java.util.Set;
@Getter
@Log4j2 @Log4j2
public class ResourcePackManager { public class ResourcePackManager {
@Getter private final ResourcePack resourcePack; private final ResourcePack resourcePack;
private final GlyphResourcePack glyphResourcePack;
public ResourcePackManager() { public ResourcePackManager() {
resourcePack = ResourcePack.resourcePack(); resourcePack = ResourcePack.resourcePack();
resourcePack.packMeta(34, "Dragonestia MSB3 - Resource pack"); resourcePack.packMeta(34, "Dragonestia MSB3 - Resource pack");
resourcePack.icon(Writable.resource(getClass().getClassLoader(), "logo.png")); resourcePack.icon(Writable.resource(getClass().getClassLoader(), "logo.png"));
resourcePack.unknownFile("credits.txt", Writable.stringUtf8("dragonestia.ru")); 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() { public void compile() {
glyphResourcePack.writeAll(resourcePack);
generateAtlases();
MinecraftResourcePackWriter.minecraft().writeToZipFile(new File("resource-pack.zip"), resourcePack); MinecraftResourcePackWriter.minecraft().writeToZipFile(new File("resource-pack.zip"), resourcePack);
log.info("Compiled resource pack. File: ./resource-pack.zip"); log.info("Compiled resource pack. File: ./resource-pack.zip");
} }
private void generateAtlases() {
Atlas.Builder atlasBuilder = Atlas.atlas();
atlasBuilder.key(Key.key("minecraft", "blocks"));
Set<String> 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());
}
} }

View File

@ -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));
}
}

View File

@ -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;
}
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B