refactor: refactored Glyphs library + refactored resources

This commit is contained in:
Andrey Terentev 2025-02-04 00:44:36 +07:00
parent 4299b8efa6
commit 4877ea4547
73 changed files with 794 additions and 2395 deletions

View File

@ -1,22 +0,0 @@
package ru.dragonestia.msb3.api;
import lombok.extern.log4j.Log4j2;
import net.minestom.server.entity.GameMode;
import ru.dragonestia.msb3.api.module.FlatWorldModule;
import ru.dragonestia.msb3.api.module.MotdModule;
import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule;
@Log4j2
public class Bootstrap {
public static void main(String[] args) {
var boot = new ServerBootstrap();
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
FlatWorldModule.init(GameMode.ADVENTURE);
ResourcePackRepositoryModule.init(boot, "0.0.0.0", 7270);
boot.start("0.0.0.0", 25565);
}
}

View File

@ -1,60 +0,0 @@
package ru.dragonestia.msb3.api;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import net.minestom.server.MinecraftServer;
import ru.dragonestia.msb3.api.item.ItemPrefabManager;
import ru.dragonestia.msb3.api.item.ItemUtil;
import ru.dragonestia.msb3.api.resource.ResourcePackManager;
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",
"",
" 8b d8 .dP\"Y8 88\"\"Yb 88888 ",
" 88b d88 `Ybo.\" 88__dP .dP ",
" 88YbdP88 o.`Y8b 88\"\"Yb o `Yb ",
" 88 YY 88 8bodP' 88oodP YbodP ",
"",
"Minecraft version: " + MinecraftServer.VERSION_NAME + " (protocol: " + MinecraftServer.PROTOCOL_VERSION + ")",
"==============================================",
};
@Getter private static ServerBootstrap instance;
@Getter private static boolean started = false;
private final MinecraftServer server;
@Getter private final ResourcePackManager resourcePackManager;
@Getter private final ItemPrefabManager itemPrefabManager;
public ServerBootstrap() {
instance = this;
for (var line: LOGO) log.info(line);
server = MinecraftServer.init();
MinecraftServer.setBrandName("Dragonestia");
resourcePackManager = new ResourcePackManager();
itemPrefabManager = new ItemPrefabManager();
init();
}
public void start(String address, int port) {
started = true;
server.start(new InetSocketAddress(address, port));
}
private void init() {
ItemUtil.init();
}
}

View File

@ -0,0 +1,82 @@
package ru.dragonestia.msb3.api.boot;
import lombok.extern.log4j.Log4j2;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.GameMode;
import net.minestom.server.event.player.PlayerChatEvent;
import ru.dragonestia.msb3.api.module.FlatWorldModule;
import ru.dragonestia.msb3.api.module.MotdModule;
import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule;
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
import ru.dragonestia.msb3.api.talk.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.talk.dialogue.DialogueTheme;
import team.unnamed.creative.ResourcePack;
@Log4j2
public class DefaultBootstrap extends ServerInitializer {
public static void main(String[] args) {
ServerBootstrap.start("0.0.0.0", 25565, new DefaultBootstrap());
}
@Override
public void onDefaultModulesLoaded() {
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
FlatWorldModule.init(GameMode.ADVENTURE);
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
var player = event.getPlayer();
var render = new DialogueRenderer(player, DialogueTheme.builder().build());
render.setText("""
Абсолютно точно.
Я знаю точнo - невозможное возможно
Сойти с ума, влюбиться так неосторoжно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
А где тебя искать, прошу ты мне ответь
В какие города мне за тобой лететь
Я готов на край Земли, я всё должен объяснить
Пойми, что без тебя я не умею жить
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а
Всё готов делить, с тобой я пополам
Ты только мне поверь, я сделал выбор сам
Дай же мне последний шанс, я всё должен объяснить
Пойми, что без тебя я не умею жить
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а
Я знаю точно - невозможное возможно
Сойти с ума, влюбиться так неосторожно
Найти тебя, не отпускать ни днём, ни ночью
Всё невозможное - возможно, знаю точно!
На-на-на-на (на-на-на-на), а-а, а-а
На-на-на-на (на-на-на-на), а-а, а-а""");
render.setButton(ButtonNumber.BUTTON_1, "Hello world!\nHello world!\nHello world!\nHello world!\nHello world!", ctx -> {});
render.setButton(ButtonNumber.BUTTON_2, "I am a teapot", ctx -> {});
render.setButton(ButtonNumber.BUTTON_3, "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nZaebumba", ctx -> {});
render.setButton(ButtonNumber.BUTTON_4, "msb3 is top!", ctx -> {});
render.show();
});
}
@Override
public void onInitializeResources(ResourcePack resourcePack) {
}
@Override
public void onResourcePackCompiled(ResourcePack resourcePack) {
ResourcePackRepositoryModule.init("0.0.0.0", 7270);
}
}

View File

@ -0,0 +1,143 @@
package ru.dragonestia.msb3.api.boot;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import lombok.extern.log4j.Log4j2;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import ru.dragonestia.msb3.api.item.ItemUtil;
import ru.dragonestia.msb3.api.resource.DialogueResources;
import ru.dragonestia.msb3.api.resource.MonologueResources;
import ru.dragonestia.msb3.api.title.BlackScreen;
import ru.dragonestia.msb3.api.util.ResourceFromJar;
import ru.dragonestia.msb3.resource.Resources;
import ru.dragonestia.msb3.resource.glyph.GlyphCharacterFactory;
import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
import ru.dragonestia.msb3.resource.utils.ClassPreLoader;
import team.unnamed.creative.lang.Language;
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
@Log4j2
@UtilityClass
public final class ServerBootstrap {
public static final ClassLoader CLASS_LOADER = ServerBootstrap.class.getClassLoader();
private static final String[] LOGO = {
"==============================================",
"Minestom Server Base v3 - by ScarletRedMan",
"",
" 8b d8 .dP\"Y8 88\"\"Yb 88888 ",
" 88b d88 `Ybo.\" 88__dP .dP ",
" 88YbdP88 o.`Y8b 88\"\"Yb o `Yb ",
" 88 YY 88 8bodP' 88oodP YbodP ",
"",
"Minecraft version: " + MinecraftServer.VERSION_NAME + " (protocol: " + MinecraftServer.PROTOCOL_VERSION + ")",
"==============================================",
};
@Getter private boolean started = false;
private final MinecraftServer server = preInitialize();
private MinecraftServer preInitialize() {
for (var line: LOGO) log.info(line);
var server = MinecraftServer.init();
MinecraftServer.setBrandName("Dragonestia");
return server;
}
public synchronized void start(String address, int port, ServerInitializer initializer) {
if (started) return;
started = true;
init(initializer);
server.start(new InetSocketAddress(address, port));
initializer.onServerStarted();
}
private void init(ServerInitializer initializer) {
initializer.onLoad();
initDefaultModules();
initializer.onDefaultModulesLoaded();
initDefaultGlyphs();
initializer.onInitializeResources(Resources.getResourcePack());
compileResourcePack();
initializer.onResourcePackCompiled(Resources.getResourcePack());
}
private void initDefaultModules() {
ItemUtil.init();
}
private void compileResourcePack() {
var resourcePack = Resources.compile();
log.info("Used {}/{} glyphs", GlyphCharacterFactory.countUsedChars(), GlyphCharacterFactory.countCharRange());
var file = new File("./resource-pack.zip");
MinecraftResourcePackWriter.minecraft().writeToZipFile(file, resourcePack);
log.info("Compiled resource pack. File: ./resource-pack.zip");
}
private void initDefaultGlyphs() {
ClassPreLoader.preload(BlackScreen.class);
MonologueResources.registerAvatar(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png"));
MonologueResources.registerFrame(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/avatar_frame.png"));
DialogueResources.registerAvatar(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/default_avatar.png"));
DialogueResources.registerAvatarFrame(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/avatar_frame.png"));
DialogueResources.registerScrollUp(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_up_button.png"));
DialogueResources.registerScrollDown(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_down_button.png"));
DialogueResources.registerBackground(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/background.png"));
DialogueResources.registerSubstrate(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/phrase_substrate.png"));
DialogueResources.registerActiveTextField(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_active_text_field.png"));
DialogueResources.registerNotActiveTextField(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_not_active_text_field.png"));
DialogueResources.registerButton(DialogueResources.DEFAULT + 1, ResourceFromJar.of("glyphs/dialogue/answer_button_active_1.png"));
DialogueResources.registerButton(DialogueResources.DEFAULT + 2, ResourceFromJar.of("glyphs/dialogue/answer_button_active_2.png"));
DialogueResources.registerButton(DialogueResources.DEFAULT + 3, ResourceFromJar.of("glyphs/dialogue/answer_button_active_3.png"));
DialogueResources.registerButton(DialogueResources.DEFAULT + 4, ResourceFromJar.of("glyphs/dialogue/answer_button_active_4.png"));
initDialogueFont();
initLocales();
}
private void initDialogueFont() {
MinecraftFont.compileFontProvider(8, -28);
MinecraftFont.compileFontProvider(8, -37);
MinecraftFont.compileFontProvider(8, -46);
MinecraftFont.compileFontProvider(8, -55);
MinecraftFont.compileFontProvider(8, -64);
MinecraftFont.compileFontProvider(8, -73);
MinecraftFont.compileFontProvider(8, -82);
MinecraftFont.compileFontProvider(8, -122);
MinecraftFont.compileFontProvider(8, -131);
MinecraftFont.compileFontProvider(8, -140);
MinecraftFont.compileFontProvider(8, -149);
MinecraftFont.compileFontProvider(8, -170);
MinecraftFont.compileFontProvider(8, -179);
MinecraftFont.compileFontProvider(8, -188);
MinecraftFont.compileFontProvider(8, -197);
}
private void initLocales() {
var locales = new HashMap<String, String>();
locales.put("container.inventory", " ");
for (var lang: List.of("en_us", "ru_ru")) {
Resources.getResourcePack().language(Language.language(Key.key("minecraft", lang), locales));
}
}
}

View File

@ -0,0 +1,42 @@
package ru.dragonestia.msb3.api.boot;
import team.unnamed.creative.ResourcePack;
/**
* Класс инициализатора сервера. Здесь имеются все точки состояний сервера.
* Создан для того чтобы было проще прописывать логику инициализации сервера
*/
public abstract class ServerInitializer {
/**
* Запускается при самом старте приложения, когда никакая конфигурация сервера еще не подгружена
*/
public void onLoad() {}
/**
* Запускается после инициализации всех стандартных модулей сервера
*/
public abstract void onDefaultModulesLoaded();
/**
* Запускается на стадии подготовки всех ресурсов, которые необходимо скомпилировать и добавить в ресурс-пак.
*
* <p>Именно на данном этапе необходимо регистрировать глифы.
* @param resourcePack Ресурс-пак
*/
public void onInitializeResources(ResourcePack resourcePack) {}
/**
* Запускается после того как все пакеты ресурсов были успешно скомпилированы и готовы к последующей упаковке
* и отправке клиенту игрока.
*
* <p> Сервер в данном состоянии еще не полностью загружен
* @param resourcePack Скомпилированный ресурс-пак
*/
public void onResourcePackCompiled(ResourcePack resourcePack) {}
/**
* Сервер полностью запущен и доступен для игры
*/
public void onServerStarted() {}
}

View File

@ -1,7 +0,0 @@
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

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

View File

@ -1,51 +0,0 @@
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

@ -1,45 +0,0 @@
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

@ -1,20 +0,0 @@
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

@ -1,26 +0,0 @@
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

@ -1,66 +0,0 @@
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")),
MINECRAFT_FONT_IMAGE_WRITABLE,
List.of(new TextureProperties(12,-6),
new TextureProperties(8,-24),
new TextureProperties(8,8),
new TextureProperties(8,-36)));
private GlyphFont() {}
public static Collection<ResourcePackPart> compile() {
return GlyphCompiler.instance().compile(SPACES, LANGUAGE_GLYPH);
}
public static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(@NotNull Key fontKey, @NotNull Key textureKey, @NotNull List<@NotNull TextureProperties> propertiesList) {
return minecraftFontGlyphCollection(fontKey, textureKey, MINECRAFT_FONT_IMAGE_WRITABLE, propertiesList);
}
public static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(
@NotNull Key fontKey,
@NotNull Key textureKey,
@NotNull Writable writable,
@NotNull List<@NotNull TextureProperties> propertiesList
) {
return LanguageGlyphCollection.of(fontKey,
Texture.texture(textureKey, writable),
propertiesList,
List.of(" АБВГДЕЖЗИК",
"ЛМНОПРСТУФХЦЧШЩЪ",
"ЫЬЭЮЯабвгдежзикл",
"мнопрстуфхцчшщъы",
"ьэюяйЙёЁ ",
"₽!\"#$%&'()*+,-./",
"0123456789: <=>?",
"@ABCDEFGHIJKLMNO",
"PQRSTUVWXYZ[\\]^_",
"`abcdefghijklmno",
"pqrstuvwxyz{|} ")
);
}
}

View File

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

View File

@ -1,24 +0,0 @@
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

@ -1,21 +0,0 @@
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 = "msb3";
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

@ -1,60 +0,0 @@
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

@ -1,86 +0,0 @@
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

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

View File

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

View File

@ -1,32 +0,0 @@
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);
}
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

@ -1,105 +0,0 @@
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

@ -1,11 +0,0 @@
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

@ -1,45 +0,0 @@
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.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;
@NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull Component text) 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

@ -1,108 +0,0 @@
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.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 ru.dragonestia.msb3.api.glyph.util.KyoriUtil;
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(properties.toString());
}
return propertiesToMulticharacterMap.get(properties).translate(text, color);
}
public @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull Component component) throws IllegalArgumentException {
var property = new TextureProperties(height, ascent);
var glyphCollection = Objects.requireNonNull(propertiesToMulticharacterMap.get(property), property.toString());
var result = new ArrayList<AppendableGlyph>();
List<? extends KyoriUtil.ColoredPartsFlattenerListener.ColoredComponentTextPart> textAndColors = KyoriUtil.toColoredParts(component);
for (var parts: textAndColors) {
result.addAll(glyphCollection.translate(parts.text(), parts.color()));
}
return Collections.unmodifiableList(result);
}
}

View File

@ -1,54 +0,0 @@
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

@ -1,131 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,64 +0,0 @@
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

@ -1,24 +0,0 @@
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

@ -1,91 +0,0 @@
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

@ -1,40 +0,0 @@
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

@ -1,10 +0,0 @@
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

@ -1,18 +0,0 @@
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

@ -1,60 +0,0 @@
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

@ -1,63 +0,0 @@
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. Provided key: " + id.key());
}
ResourceProducer producer = compiled.get(id.key());
if (!id.getType().isAssignableFrom(producer.getClass())) {
throw new IllegalArgumentException("Wrong producer type");
}
return (T) producer;
}
}

View File

@ -1,58 +0,0 @@
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

@ -1,12 +0,0 @@
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

@ -1,19 +0,0 @@
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

@ -1,37 +0,0 @@
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

@ -1,19 +0,0 @@
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

@ -1,28 +0,0 @@
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

@ -1,57 +0,0 @@
package ru.dragonestia.msb3.api.glyph.util;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.flattener.FlattenerListener;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
@UtilityClass
public class KyoriUtil {
public @NotNull List<? extends ColoredPartsFlattenerListener.ColoredComponentTextPart> toColoredParts(@NotNull Component component) {
var listener = new ColoredPartsFlattenerListener();
ComponentFlattener.basic().flatten(component, listener);
return listener.result();
}
public static class ColoredPartsFlattenerListener implements FlattenerListener {
private final Deque<TextColor> colors = new LinkedList<>();
private final List<ColoredComponentTextPart> result = new ArrayList<>();
public void pushStyle(@NotNull Style style) {
var color = style.color();
if (color != null) colors.add(color);
}
public void component(@NotNull String text) {
result.add(new ColoredComponentTextPart(text, current()));
}
public void popStyle(@NotNull Style style) {
var color = style.color();
if (color != null) colors.removeLast();
}
private TextColor current() {
var color = colors.peekLast();
return color != null ? color : NamedTextColor.WHITE;
}
public @NotNull List<? extends @NotNull ColoredComponentTextPart> result() {
return result;
}
public record ColoredComponentTextPart(String text, TextColor color) {}
}
}

View File

@ -3,8 +3,7 @@ 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.boot.ServerBootstrap;
import ru.dragonestia.msb3.api.util.ResourceFromJar;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.model.*;
@ -32,7 +31,7 @@ public class BlankSlotItem {
}
public static Collection<ResourcePackPart> compile() {
var modelKey = Key.key(Glyph.DEFAULT_NAMESPACE, "blank_slot");
var modelKey = Key.key("msb3", "blank_slot");
var itemKey = Key.key("item/paper");
Model blankSlotModel = Model.model()
@ -52,7 +51,7 @@ public class BlankSlotItem {
.overrides(ItemOverride.of(modelKey, ItemPredicate.customModelData(1)))
.build();
Texture texture = Texture.texture(Key.key(Glyph.DEFAULT_NAMESPACE, "blank_slot.png"), BLANK_SLOT_IMAGE_WRITABLE);
Texture texture = Texture.texture(Key.key("msb3", "blank_slot.png"), BLANK_SLOT_IMAGE_WRITABLE);
return Arrays.asList(blankSlotModel, paperItemModel, texture);
}
}

View File

@ -1,7 +1,8 @@
package ru.dragonestia.msb3.api.item;
import lombok.experimental.UtilityClass;
import lombok.extern.log4j.Log4j2;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.boot.ServerBootstrap;
import ru.dragonestia.msb3.api.item.prefab.ItemPrefab;
import java.util.HashMap;
@ -9,12 +10,11 @@ import java.util.Map;
import java.util.Optional;
@Log4j2
@UtilityClass
public final class ItemPrefabManager {
private final Map<String, ItemPrefab> prefabs = new HashMap<>();
public ItemPrefabManager() {}
public synchronized void register(ItemPrefab prefab) {
if (ServerBootstrap.isStarted()) {
throw new IllegalStateException("Server is already started");

View File

@ -3,7 +3,7 @@ package ru.dragonestia.msb3.api.item.prefab;
import net.kyori.adventure.nbt.BinaryTag;
import net.minestom.server.item.ItemStack;
import net.minestom.server.tag.Tag;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.item.ItemPrefabManager;
import java.util.Optional;
import java.util.function.Supplier;
@ -22,12 +22,12 @@ public class ItemPrefab {
}
public static Optional<ItemPrefab> of(String identifier) {
return ServerBootstrap.getInstance().getItemPrefabManager().getPrefab(identifier);
return ItemPrefabManager.getPrefab(identifier);
}
public static Optional<ItemPrefab> of(ItemStack item) {
if (item == null || item.hasTag(TAG_IDENTIFIER)) return Optional.empty();
return ServerBootstrap.getInstance().getItemPrefabManager().getPrefab(item.getTag(TAG_IDENTIFIER));
return ItemPrefabManager.getPrefab(item.getTag(TAG_IDENTIFIER));
}
public final String getIdentifier() {

View File

@ -1,13 +1,13 @@
package ru.dragonestia.msb3.api.module;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.resource.ResourcePackCallback;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.resource.ResourcePackRequest;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.event.player.PlayerSpawnEvent;
import ru.dragonestia.msb3.api.ServerBootstrap;
import team.unnamed.creative.BuiltResourcePack;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.server.ResourcePackServer;
@ -22,19 +22,16 @@ import java.util.UUID;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
@UtilityClass
public class ResourcePackRepositoryModule {
private static boolean used = false;
private ResourcePackRepositoryModule() {}
private boolean used = false;
@SneakyThrows
public static synchronized void init(ServerBootstrap bootstrap, String address, int port) {
public synchronized void init(String address, int port) {
if (used) return;
used = true;
bootstrap.getResourcePackManager().compile();
var file = new File("./resource-pack.zip");
var hash = calculateHash(file);
var uuid = UUID.randomUUID();
@ -59,7 +56,7 @@ public class ResourcePackRepositoryModule {
}
@SneakyThrows
private static String calculateHash(File file) {
private String calculateHash(File file) {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
try (InputStream fis = new FileInputStream(file)) {
@ -68,7 +65,7 @@ public class ResourcePackRepositoryModule {
}
}
private static String byteArray2Hex(final byte[] hash) {
private String byteArray2Hex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b: hash) formatter.format("%02x", b);
return formatter.toString();

View File

@ -1,21 +1,12 @@
package ru.dragonestia.msb3.api.resource;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.key.Key;
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
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.pack.GlyphResourcePack;
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
import ru.dragonestia.msb3.api.resource.dialog.Background;
import ru.dragonestia.msb3.api.resource.dialog.Button;
import ru.dragonestia.msb3.api.resource.dialog.Substrate;
import ru.dragonestia.msb3.api.resource.dialog.TextField;
import ru.dragonestia.msb3.api.talk.dialogue.DialogGlyphPositions;
import ru.dragonestia.msb3.api.util.ImageUtil;
import ru.dragonestia.msb3.api.util.ResourceFromJar;
import ru.dragonestia.msb3.api.resource.dialog.*;
import ru.dragonestia.msb3.resource.Resources;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
import ru.dragonestia.msb3.resource.utils.ImageUtils;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.texture.Texture;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@ -24,95 +15,59 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;
import java.util.stream.Stream;
@UtilityClass
public class DialogueResources {
private static final Key DIALOGUE_FONT_KEY = Key.key("msb3", "dialog");
public static final String DEFAULT = "default";
private final GlyphResourcePack glyphResourcePack;
private final Map<String, GlyphEntry> avatars = new HashMap<>();
private final Map<String, GlyphEntry> avatarFrames = new HashMap<>();
private final Map<String, GlyphEntry> scrollUp = new HashMap<>();
private final Map<String, GlyphEntry> scrollDown = new HashMap<>();
private final Map<String, GlyphImage> avatars = new HashMap<>();
private final Map<String, GlyphImage> avatarFrames = new HashMap<>();
private final Map<String, GlyphImage> scrollUp = new HashMap<>();
private final Map<String, GlyphImage> scrollDown = new HashMap<>();
private final Map<String, Background> backgrounds = new HashMap<>();
private final Map<String, Substrate> substrates = new HashMap<>();
private final Map<String, FontGlyphEntry> fonts = new HashMap<>();
private final Map<String, Button> buttons = new HashMap<>();
private final Map<String, TextField> activeFields = new HashMap<>();
private final Map<String, TextField> notActiveFields = new HashMap<>();
public DialogueResources(GlyphResourcePack glyphResourcePack) {
this.glyphResourcePack = glyphResourcePack;
registerAvatar(DEFAULT, ResourceFromJar.of("glyphs/dialogue/default_avatar.png"));
registerAvatarFrame(DEFAULT, ResourceFromJar.of("glyphs/dialogue/avatar_frame.png"));
registerScrollTextUp(DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_up_button.png"));
registerScrollTextDown(DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_down_button.png"));
registerBackground(DEFAULT, ResourceFromJar.of("glyphs/dialogue/background.png"));
registerSubstrate(DEFAULT, ResourceFromJar.of("glyphs/dialogue/phrase_substrate.png"));
registerActiveTextField(DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_active_text_field.png"));
registerNotActiveTextField(DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_not_active_text_field.png"));
registerButton(DEFAULT + 1, ResourceFromJar.of("glyphs/dialogue/answer_button_active_1.png"));
registerButton(DEFAULT + 2, ResourceFromJar.of("glyphs/dialogue/answer_button_active_2.png"));
registerButton(DEFAULT + 3, ResourceFromJar.of("glyphs/dialogue/answer_button_active_3.png"));
registerButton(DEFAULT + 4, ResourceFromJar.of("glyphs/dialogue/answer_button_active_4.png"));
registerFont(DEFAULT, ResourceFromJar.of("glyphs/defaults/minecraft_font.png"));
}
public void compile(GlyphResourcePack resourcePack) {}
public void registerAvatar(String identifier, Writable writable) {
registerAvatar(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerAvatar(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph = createGlyph(identifier, writable, "avatar", positions.avatar().height(), positions.avatar().y());
var glyphIdentifier = StringIdentifier.image("dialog_avatar__" + identifier);
avatars.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
avatars.put(identifier, Resources.createGlyph(
Key.key("msb3", "dialogue/avatar/" + identifier),
writable,
GlyphPositions.avatar.height(),
GlyphPositions.avatar.y()
));
}
public void registerAvatarFrame(String identifier, Writable writable) {
registerAvatarFrame(identifier, writable, DialogGlyphPositions.DEFAULT);
avatarFrames.put(identifier, Resources.createGlyph(
Key.key("msb3", "dialogue/avatar_frame/" + identifier),
writable,
GlyphPositions.avatar.frameHeight(),
GlyphPositions.avatar.y() + GlyphPositions.avatar.frameBorderSize()
));
}
public void registerAvatarFrame(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph = createGlyph(identifier, writable, "avatar_frame", positions.avatar().frameHeight(), positions.avatar().y() + positions.avatar().frameBorderSize());
var glyphIdentifier = StringIdentifier.image("dialog_avatar_frame__" + identifier);
avatarFrames.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
public void registerScrollUp(String identifier, Writable writable) {
scrollUp.put(identifier, Resources.createGlyph(
Key.key("msb3", "dialogue/scroll_up/" + identifier),
writable,
GlyphPositions.scrollPhraseButton.height(),
GlyphPositions.scrollPhraseButton.buttonY()
));
}
public void registerScrollTextUp(String identifier, Writable writable) {
registerScrollTextUp(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerScrollTextUp(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph = createGlyph(identifier, writable, "scroll_up", positions.scrollPhraseButton().height(), positions.scrollPhraseButton().buttonY());
var glyphIdentifier = StringIdentifier.image("dialog_scroll_up__" + identifier);
scrollUp.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
}
public void registerScrollTextDown(String identifier, Writable writable) {
registerScrollTextDown(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerScrollTextDown(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph = createGlyph(identifier, writable, "scroll_down", positions.scrollPhraseButton().height(), positions.scrollPhraseButton().buttonY());
var glyphIdentifier = StringIdentifier.image("dialog_scroll_down__" + identifier);
scrollDown.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
public void registerScrollDown(String identifier, Writable writable) {
scrollDown.put(identifier, Resources.createGlyph(
Key.key("msb3", "dialogue/scroll_down/" + identifier),
writable,
GlyphPositions.scrollPhraseButton.height(),
GlyphPositions.scrollPhraseButton.buttonY()
));
}
public void registerBackground(String identifier, Writable writable) {
registerBackground(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerBackground(String identifier, Writable writable, DialogGlyphPositions positions) {
BufferedImage image;
Writable part1;
Writable part2;
@ -124,43 +79,43 @@ public class DialogueResources {
var w = image.getWidth();
var h = image.getHeight();
part1 = ImageUtil.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2));
part2 = ImageUtil.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2));
part3 = ImageUtil.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2));
part4 = ImageUtil.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2));
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2));
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2));
part3 = ImageUtils.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2));
part4 = ImageUtils.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
var glyph1 = new GlyphEntry(
StringIdentifier.image("dialog_background1__" + identifier),
createGlyph(identifier, part1, "background1", positions.guiBackground().height() / 2, positions.guiBackground().topPartsY())
var glyph1 = Resources.createGlyph(
Key.key("msb3", "dialogue/background/" + identifier + "__1"),
part1,
GlyphPositions.guiBackground.height() / 2,
GlyphPositions.guiBackground.topPartsY()
);
var glyph2 = new GlyphEntry(
StringIdentifier.image("dialog_background2__" + identifier),
createGlyph(identifier, part2, "background2", positions.guiBackground().height() / 2, positions.guiBackground().topPartsY())
var glyph2 = Resources.createGlyph(
Key.key("msb3", "dialogue/background/" + identifier + "__2"),
part2,
GlyphPositions.guiBackground.height() / 2,
GlyphPositions.guiBackground.topPartsY()
);
var glyph3 = new GlyphEntry(
StringIdentifier.image("dialog_background3__" + identifier),
createGlyph(identifier, part3, "background3", positions.guiBackground().height() / 2, positions.guiBackground().bottomPartsY())
var glyph3 = Resources.createGlyph(
Key.key("msb3", "dialogue/background/" + identifier + "__3"),
part3,
GlyphPositions.guiBackground.height() / 2,
GlyphPositions.guiBackground.bottomPartsY()
);
var glyph4 = new GlyphEntry(
StringIdentifier.image("dialog_background4__" + identifier),
createGlyph(identifier, part4, "background4", positions.guiBackground().height() / 2, positions.guiBackground().bottomPartsY())
var glyph4 = Resources.createGlyph(
Key.key("msb3", "dialogue/background/" + identifier + "__4"),
part4,
GlyphPositions.guiBackground.height() / 2,
GlyphPositions.guiBackground.bottomPartsY()
);
backgrounds.put(identifier, new Background(glyph1.glyph(), glyph2.glyph(), glyph3.glyph(), glyph4.glyph()));
glyphResourcePack.with(glyph1.identifier(), glyph1.glyph());
glyphResourcePack.with(glyph2.identifier(), glyph2.glyph());
glyphResourcePack.with(glyph3.identifier(), glyph3.glyph());
glyphResourcePack.with(glyph4.identifier(), glyph4.glyph());
backgrounds.put(identifier, new Background(glyph1, glyph2, glyph3, glyph4));
}
public void registerSubstrate(String identifier, Writable writable) {
registerSubstrate(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerSubstrate(String identifier, Writable writable, DialogGlyphPositions positions) {
BufferedImage image;
Writable part1;
Writable part2;
@ -170,168 +125,129 @@ public class DialogueResources {
var w = image.getWidth();
var h = image.getHeight();
part1 = ImageUtil.imageToWritable(image.getSubimage(0, 0, w / 2, h));
part2 = ImageUtil.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h));
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h));
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
var glyph1 = new GlyphEntry(
StringIdentifier.image("dialog_substrate1__" + identifier),
createGlyph(identifier, part1, "substrate1", positions.phraseSubstrate().height(), positions.phraseSubstrate().y())
var glyph1 = Resources.createGlyph(
Key.key("msb3", "dialogue/substrate/" + identifier + "__1"),
part1,
GlyphPositions.phraseSubstrate.height(),
GlyphPositions.phraseSubstrate.y()
);
var glyph2 = new GlyphEntry(
StringIdentifier.image("dialog_substrate2__" + identifier),
createGlyph(identifier, part2, "substrate2", positions.phraseSubstrate().height(), positions.phraseSubstrate().y())
var glyph2 = Resources.createGlyph(
Key.key("msb3", "dialogue/substrate/" + identifier + "__2"),
part2,
GlyphPositions.phraseSubstrate.height(),
GlyphPositions.phraseSubstrate.y()
);
substrates.put(identifier, new Substrate(glyph1.glyph(), glyph2.glyph()));
glyphResourcePack.with(glyph1.identifier(), glyph1.glyph());
glyphResourcePack.with(glyph2.identifier(), glyph2.glyph());
}
public void registerFont(String identifier, Writable writable) {
registerFont(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerFont(String identifier, Writable writable, DialogGlyphPositions positions) {
var propertiesList = Stream.concat(Stream.concat(IntStream.range(0, positions.phraseText().maxLines()).map((idx) -> {
return idx * (positions.phraseText().fontHeight() + 1) + positions.phraseText().firstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.phraseText().fontHeight(), ascent * -1);
}), IntStream.range(0, positions.answerText().maxLines()).map((idx) -> {
return idx * (positions.answerText().fontHeight() + 1) + positions.answerText().topFirstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.answerText().fontHeight(), ascent * -1);
})), IntStream.range(0, positions.answerText().maxLines()).map((idx) -> {
return idx * (positions.answerText().fontHeight() + 1) + positions.answerText().bottomFirstLineAscent() * -1;
}).boxed().map((ascent) -> {
return new TextureProperties(positions.answerText().fontHeight(), ascent * -1);
})).toList();
var glyphIdentifier = StringIdentifier.of("dialog_font", LanguageGlyphCollection.class);
var font = GlyphFont.minecraftFontGlyphCollection(DIALOGUE_FONT_KEY, Key.key("dialog/font.png"), writable, propertiesList);
fonts.put(identifier, new FontGlyphEntry(glyphIdentifier, font));
glyphResourcePack.with(glyphIdentifier, font);
substrates.put(identifier, new Substrate(glyph1, glyph2));
}
public void registerButton(String identifier, Writable writable) {
registerButton(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerButton(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph1 = createGlyph(identifier,
var glyph1 = Resources.createGlyph(
Key.key("msb3", "dialogue/button/" + identifier),
writable,
"button1",
positions.answerButton().height(),
positions.answerButton().topButtonY());
var glyph2 = createGlyph(identifier,
GlyphPositions.answerButton.height(),
GlyphPositions.answerButton.topButtonY()
);
var glyph2 = Resources.createGlyph(
Key.key("msb3", "dialogue/button/" + identifier),
writable,
"button2",
positions.answerButton().height(),
positions.answerButton().topButtonY());
var glyph3 = createGlyph(identifier,
GlyphPositions.answerButton.height(),
GlyphPositions.answerButton.topButtonY()
);
var glyph3 = Resources.createGlyph(
Key.key("msb3", "dialogue/button/" + identifier),
writable,
"button3",
positions.answerButton().height(),
positions.answerButton().bottomButtonY());
var glyph4 = createGlyph(identifier,
GlyphPositions.answerButton.height(),
GlyphPositions.answerButton.bottomButtonY()
);
var glyph4 = Resources.createGlyph(
Key.key("msb3", "dialogue/button/" + identifier),
writable,
"button4",
positions.answerButton().height(),
positions.answerButton().bottomButtonY());
GlyphPositions.answerButton.height(),
GlyphPositions.answerButton.bottomButtonY()
);
buttons.put(identifier, new Button(glyph1, glyph2, glyph3, glyph4));
glyphResourcePack
.with(StringIdentifier.image("dialog_button1__" + identifier), glyph1)
.with(StringIdentifier.image("dialog_button2__" + identifier), glyph2)
.with(StringIdentifier.image("dialog_button3__" + identifier), glyph3)
.with(StringIdentifier.image("dialog_button4__" + identifier), glyph4);
}
public void registerActiveTextField(String identifier, Writable writable) {
registerActiveTextField(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerActiveTextField(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph1 = createGlyph(identifier,
var glyph1 = Resources.createGlyph(
Key.key("msb3", "dialogue/text_field/" + identifier),
writable,
"active_field1",
positions.answerField().height(),
positions.answerField().topFieldY());
var glyph2 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.topFieldY()
);
var glyph2 = Resources.createGlyph(
Key.key("msb3", "dialogue/text_field/" + identifier),
writable,
"active_field2",
positions.answerField().height(),
positions.answerField().topFieldY());
var glyph3 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.topFieldY()
);
var glyph3 = Resources.createGlyph(
Key.key("msb3", "dialogue/text_field/" + identifier),
writable,
"active_field3",
positions.answerField().height(),
positions.answerField().bottomFieldY());
var glyph4 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.bottomFieldY()
);
var glyph4 = Resources.createGlyph(
Key.key("msb3", "dialogue/text_field/" + identifier),
writable,
"active_field4",
positions.answerField().height(),
positions.answerField().bottomFieldY());
GlyphPositions.answerField.height(),
GlyphPositions.answerField.bottomFieldY()
);
activeFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4));
glyphResourcePack
.with(StringIdentifier.image("active_field1__" + identifier), glyph1)
.with(StringIdentifier.image("active_field2__" + identifier), glyph2)
.with(StringIdentifier.image("active_field3__" + identifier), glyph3)
.with(StringIdentifier.image("active_field4__" + identifier), glyph4);
}
public void registerNotActiveTextField(String identifier, Writable writable) {
registerNotActiveTextField(identifier, writable, DialogGlyphPositions.DEFAULT);
}
public void registerNotActiveTextField(String identifier, Writable writable, DialogGlyphPositions positions) {
var glyph1 = createGlyph(identifier,
var glyph1 = Resources.createGlyph(
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
writable,
"not_active_field1",
positions.answerField().height(),
positions.answerField().topFieldY());
var glyph2 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.topFieldY()
);
var glyph2 = Resources.createGlyph(
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
writable,
"not_active_field2",
positions.answerField().height(),
positions.answerField().topFieldY());
var glyph3 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.topFieldY()
);
var glyph3 = Resources.createGlyph(
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
writable,
"not_active_field3",
positions.answerField().height(),
positions.answerField().bottomFieldY());
var glyph4 = createGlyph(identifier,
GlyphPositions.answerField.height(),
GlyphPositions.answerField.bottomFieldY()
);
var glyph4 = Resources.createGlyph(
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
writable,
"not_active_field4",
positions.answerField().height(),
positions.answerField().bottomFieldY());
GlyphPositions.answerField.height(),
GlyphPositions.answerField.bottomFieldY()
);
notActiveFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4));
glyphResourcePack
.with(StringIdentifier.image("not_active_field1__" + identifier), glyph1)
.with(StringIdentifier.image("not_active_field2__" + identifier), glyph2)
.with(StringIdentifier.image("not_active_field3__" + identifier), glyph3)
.with(StringIdentifier.image("not_active_field4__" + identifier), glyph4);
}
public Optional<ImageGlyph> getAvatar(String identifier) {
return getGlyphFromMap(identifier, avatars);
public Optional<GlyphImage> getAvatar(String identifier) {
return Optional.ofNullable(avatars.get(identifier));
}
public Optional<ImageGlyph> getAvatarFrame(String identifier) {
return getGlyphFromMap(identifier, avatarFrames);
public Optional<GlyphImage> getAvatarFrame(String identifier) {
return Optional.ofNullable(avatarFrames.get(identifier));
}
public Optional<ImageGlyph> getScrollUp(String identifier) {
return getGlyphFromMap(identifier, scrollUp);
public Optional<GlyphImage> getScrollUp(String identifier) {
return Optional.ofNullable(scrollUp.get(identifier));
}
public Optional<ImageGlyph> getScrollDown(String identifier) {
return getGlyphFromMap(identifier, scrollDown);
public Optional<GlyphImage> getScrollDown(String identifier) {
return Optional.ofNullable(scrollDown.get(identifier));
}
public Optional<Background> getBackground(String identifier) {
@ -342,10 +258,6 @@ public class DialogueResources {
return Optional.ofNullable(substrates.get(identifier));
}
public Optional<LanguageGlyphCollection> getFont(String identifier) {
return Optional.ofNullable(fonts.get(identifier)).map(FontGlyphEntry::glyph);
}
public Optional<Button> getButton(String identifier) {
return Optional.ofNullable(buttons.get(identifier));
}
@ -357,21 +269,4 @@ public class DialogueResources {
public Optional<TextField> getNotActiveTextField(String identifier) {
return Optional.ofNullable(notActiveFields.get(identifier));
}
private ImageGlyph createGlyph(String identifier, Writable writable, String element, int height, int ascent) {
return ImageGlyph.of(DIALOGUE_FONT_KEY,
Texture.texture().key(Key.key("msb3", "dialog/" + identifier + "/" + element + ".png")).data(writable).build(),
new TextureProperties(height, ascent));
}
private Optional<ImageGlyph> getGlyphFromMap(String identifier, Map<String, GlyphEntry> map) {
return Optional.ofNullable(map.get(identifier))
.map(entry -> {
try {
return glyphResourcePack.get(entry.identifier());
} catch (IllegalArgumentException ex) {
return null;
}
});
}
}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.msb3.api.resource;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection;
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
public record FontGlyphEntry(StringIdentifier<@NotNull LanguageGlyphCollection> identifier, LanguageGlyphCollection glyph) {}

View File

@ -1,7 +0,0 @@
package ru.dragonestia.msb3.api.resource;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
public record GlyphEntry(StringIdentifier<@NotNull ImageGlyph> identifier, ImageGlyph glyph) {}

View File

@ -1,97 +1,58 @@
package ru.dragonestia.msb3.api.resource;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.key.Key;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
import ru.dragonestia.msb3.api.util.ResourceFromJar;
import ru.dragonestia.msb3.resource.Resources;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.texture.Texture;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@UtilityClass
public class MonologueResources {
private static final Key MONOLOG_FONT_KEY = Key.key("msb3", "monolog");
public static final String DEFAULT = "default";
private final GlyphResourcePack glyphResourcePack;
private final Map<String, GlyphEntry> avatars = new HashMap<>();
private final Map<String, GlyphEntry> frames = new HashMap<>();
private final GlyphEntry speechIndicator = initSpeechIndicator();
public MonologueResources(GlyphResourcePack glyphResourcePack) {
this.glyphResourcePack = glyphResourcePack;
registerAvatar(DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png"));
registerFrame(DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/avatar_frame.png"));
}
private GlyphEntry initSpeechIndicator() {
var writable = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/speech_indicator.png");
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
Texture.texture().key(Key.key("msb3", "monolog/speech_indicator.png")).data(writable).build(),
new TextureProperties(8, 6));
var glyphIdentifier = StringIdentifier.image("monolog_speech_indicator");
return new GlyphEntry(glyphIdentifier, glyph);
}
private final Map<String, GlyphImage> avatars = new HashMap<>();
private final Map<String, GlyphImage> frames = new HashMap<>();
@Getter private static final GlyphImage speechIndicator = Resources.createGlyph(
Key.key("msb3", "monologue/speech_indicator"),
ResourceFromJar.of("glyphs/monologue/speech_indicator.png"),
8,
6
);
public void registerAvatar(String identifier, Writable writable) {
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
Texture.texture().key(Key.key("msb3", "monolog/avatar_" + identifier + ".png")).data(writable).build(),
new TextureProperties(42, 2));
var glyph = Resources.createGlyph(
Key.key("msb3", "monologue/avatar/" + identifier),
writable,
42,
2
);
var glyphIdentifier = StringIdentifier.image("monolog_avatar_" + identifier);
avatars.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
avatars.put(identifier, glyph);
}
public void registerFrame(String identifier, Writable writable) {
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
Texture.texture().key(Key.key("msb3", "monolog/frame_" + identifier + ".png")).data(writable).build(),
new TextureProperties(50, 6));
var glyph = Resources.createGlyph(
Key.key("msb3", "monologue/frame/" + identifier),
writable,
50,
6
);
var glyphIdentifier = StringIdentifier.image("monolog_frame_" + identifier);
frames.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
glyphResourcePack.with(glyphIdentifier, glyph);
frames.put(identifier, glyph);
}
public Optional<ImageGlyph> getAvatar(String identifier) {
return Optional.ofNullable(avatars.get(identifier))
.map(entry -> {
try {
return glyphResourcePack.get(entry.identifier());
} catch (IllegalArgumentException ex) {
return null;
}
});
public Optional<GlyphImage> getAvatar(String identifier) {
return Optional.ofNullable(avatars.get(identifier));
}
public Optional<ImageGlyph> getFrame(String identifier) {
return Optional.ofNullable(frames.get(identifier))
.map(entry -> {
try {
return glyphResourcePack.get(entry.identifier());
} catch (IllegalArgumentException ex) {
return null;
}
});
}
public ImageGlyph getSpeechIndicator() {
return glyphResourcePack.get(speechIndicator.identifier());
}
public void compile(GlyphResourcePack resourcePack) {
resourcePack.with(speechIndicator.identifier(), speechIndicator.glyph());
public Optional<GlyphImage> getFrame(String identifier) {
return Optional.ofNullable(frames.get(identifier));
}
}

View File

@ -1,98 +0,0 @@
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.talk.dialogue.DialogueRenderer;
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.lang.Language;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
import java.io.File;
import java.util.*;
@Getter
@Log4j2
public class ResourcePackManager {
private final ResourcePack resourcePack;
private final GlyphResourcePack glyphResourcePack;
private final MonologueResources monologueResources;
private final DialogueResources dialogueResources;
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();
monologueResources = new MonologueResources(glyphResourcePack);
dialogueResources = new DialogueResources(glyphResourcePack);
initDefaultGlyphs();
initLocales();
}
private void initDefaultGlyphs() {
glyphResourcePack.withMojangSpaces();
glyphResourcePack.with(BlankSlotItem.compile());
glyphResourcePack.with(GlyphCompiler.instance().compile(BlackScreen.GLYPH));
glyphResourcePack.with(GlyphFont.compile());
monologueResources.compile(glyphResourcePack);
dialogueResources.compile(glyphResourcePack);
}
private void initLocales() {
var locales = new HashMap<String, String>();
locales.put("container.inventory", " ");
for (var lang: List.of("en_us", "ru_ru")) {
resourcePack.language(Language.language(Key.key("minecraft", lang), locales));
}
}
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<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

@ -1,5 +1,10 @@
package ru.dragonestia.msb3.api.resource.dialog;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
public record Background(ImageGlyph part1, ImageGlyph part2, ImageGlyph part3, ImageGlyph part4) {}
public record Background(
GlyphImage part1,
GlyphImage part2,
GlyphImage part3,
GlyphImage part4
) {}

View File

@ -1,10 +1,15 @@
package ru.dragonestia.msb3.api.resource.dialog;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
public record Button(ImageGlyph button1, ImageGlyph button2, ImageGlyph button3, ImageGlyph button4) {
public record Button(
GlyphImage button1,
GlyphImage button2,
GlyphImage button3,
GlyphImage button4
) {
public ImageGlyph get(ButtonNumber number) {
public GlyphImage get(ButtonNumber number) {
return switch (number) {
case BUTTON_1 -> button1;
case BUTTON_2 -> button2;

View File

@ -0,0 +1,47 @@
package ru.dragonestia.msb3.api.resource.dialog;
import lombok.experimental.UtilityClass;
@UtilityClass
public class GlyphPositions {
public final GuiBackground guiBackground = new GuiBackground(35, -96, 262);
public final PhraseText phraseText = new PhraseText(11, -11, 340, 8, 8);
public final PhraseSubstrate phraseSubstrate = new PhraseSubstrate(-24, 19, 356, 116);
public final Avatar avatar = new Avatar(-144, 11, 100, 116);
public final AnswerText answerText = new AnswerText(175, 4, 8, -145, 149, -122, -170);
public final AnswerField answerField = new AnswerField(-117, -165, -153, 141, 44);
public final AnswerButton answerButton = new AnswerButton(-124, -182, 41, 97, 20);
public final ScrollPhraseButton scrollPhraseButton = new ScrollPhraseButton(0, 126, -100, 10);
public final ChestSlotsButtons chestSlotsButtons = new ChestSlotsButtons(new int[] {45, 46}, new int[] {52, 53}, new int[] {65, 66}, new int[] {68, 69}, new int[] {56, 57}, new int[] {59, 60});
public record GuiBackground(int topPartsY, int bottomPartsY, int height) {}
public record PhraseText(int maxLines, int lineX, int lineWidth, int fontHeight, int firstLineAscent) {
public TextureProperties firstLineProperties() {
return new TextureProperties(fontHeight, firstLineAscent);
}
}
public record PhraseSubstrate(int x, int y, int width, int height) {}
public record Avatar(int x, int y, int height, int frameHeight) {
public int frameBorderSize() {
return (frameHeight - height) / 2;
}
}
public record AnswerText(int lineWidth, int maxLines, int fontHeight, int leftLineX,
int rightLineX, int topFirstLineAscent, int bottomFirstLineAscent) {}
public record AnswerField(int topFieldY, int bottomFieldY, int leftFieldX, int rightFieldX, int height) {}
public record AnswerButton(int topButtonY, int bottomButtonY, int leftButtonX,
int rightButtonX, int height) {}
public record ScrollPhraseButton(int scrollUpButtonX, int scrollDownButtonX, int buttonY, int height) {}
public record ChestSlotsButtons(int[] scrollUp, int[] scrollDown, int[] answer1, int[] answer2, int[] answer3, int[] answer4) {}
}

View File

@ -1,5 +1,8 @@
package ru.dragonestia.msb3.api.resource.dialog;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
public record Substrate(ImageGlyph part1, ImageGlyph part2) {}
public record Substrate(
GlyphImage part1,
GlyphImage part2
) {}

View File

@ -1,10 +1,15 @@
package ru.dragonestia.msb3.api.resource.dialog;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
public record TextField(ImageGlyph field1, ImageGlyph field2, ImageGlyph field3, ImageGlyph field4) {
public record TextField(
GlyphImage field1,
GlyphImage field2,
GlyphImage field3,
GlyphImage field4
) {
public ImageGlyph get(ButtonNumber number) {
public GlyphImage get(ButtonNumber number) {
return switch (number) {
case BUTTON_1 -> field1;
case BUTTON_2 -> field2;

View File

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

View File

@ -1,56 +0,0 @@
package ru.dragonestia.msb3.api.talk.dialogue;
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
public record DialogGlyphPositions(GuiBackground guiBackground,
PhraseText phraseText,
PhraseSubstrate phraseSubstrate,
Avatar avatar,
AnswerText answerText,
AnswerField answerField,
AnswerButton answerButton,
ScrollPhraseButton scrollPhraseButton,
ChestSlotsButtons chestSlotsButtons) {
public static final DialogGlyphPositions DEFAULT = new DialogGlyphPositions(
new GuiBackground(35, -96, 262),
new PhraseText(11, -11, 340, 8, 8),
new PhraseSubstrate(-24, 19, 356, 116),
new Avatar(-144, 11, 100, 116),
new AnswerText(175, 4, 8, -145, 149, -122, -170),
new AnswerField(-117, -165, -153, 141, 44),
new AnswerButton(-124, -182, 41, 97, 20),
new ScrollPhraseButton(0, 126, -100, 10),
new ChestSlotsButtons(new int[] {45, 46}, new int[] {52, 53}, new int[] {65, 66}, new int[] {68, 69}, new int[] {56, 57}, new int[] {59, 60})
);
public record GuiBackground(int topPartsY, int bottomPartsY, int height) {}
public record PhraseText(int maxLines, int lineX, int lineWidth, int fontHeight, int firstLineAscent) {
public TextureProperties firstLineProperties() {
return new TextureProperties(fontHeight, firstLineAscent);
}
}
public record PhraseSubstrate(int x, int y, int width, int height) {}
public record Avatar(int x, int y, int height, int frameHeight) {
public int frameBorderSize() {
return (frameHeight - height) / 2;
}
}
public record AnswerText(int lineWidth, int maxLines, int fontHeight, int leftLineX,
int rightLineX, int topFirstLineAscent, int bottomFirstLineAscent) {}
public record AnswerField(int topFieldY, int bottomFieldY, int leftFieldX, int rightFieldX, int height) {}
public record AnswerButton(int topButtonY, int bottomButtonY, int leftButtonX,
int rightButtonX, int height) {}
public record ScrollPhraseButton(int scrollUpButtonX, int scrollDownButtonX, int buttonY, int height) {}
public record ChestSlotsButtons(int[] scrollUp, int[] scrollDown, int[] answer1, int[] answer2, int[] answer3, int[] answer4) {}
}

View File

@ -21,15 +21,16 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.tag.Tag;
import org.apache.commons.text.WordUtils;
import org.jetbrains.annotations.NotNull;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
import ru.dragonestia.msb3.api.glyph.glyph.GlyphComponentBuilder;
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
import ru.dragonestia.msb3.api.item.BlankSlotItem;
import ru.dragonestia.msb3.api.item.ItemUtil;
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
import ru.dragonestia.msb3.api.resource.dialog.TextureProperties;
import ru.dragonestia.msb3.api.util.StringUtil;
import ru.dragonestia.msb3.resource.glyph.GlyphComponent;
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
import java.util.ArrayList;
import java.util.EnumMap;
@ -42,7 +43,6 @@ public class DialogueRenderer extends Inventory {
public static final int CHEST_GUI_WIDTH = 176;
private static final Tag<String> TAG_CLICK_TYPE = Tag.String("msb3_dialogue_click_type");
@Getter private final GlyphResourcePack glyphResourcePack = ServerBootstrap.getInstance().getResourcePackManager().getGlyphResourcePack();
private final Player player;
@Getter @Setter private DialogueTheme theme;
@Getter private String text = "";
@ -74,15 +74,15 @@ public class DialogueRenderer extends Inventory {
}
public void scrollDown() {
if (maxLines > shifted + theme.getPositions().phraseText().maxLines()) {
shifted = Math.min(maxLines - theme.getPositions().phraseText().maxLines(), shifted + 3);
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
shifted = Math.min(maxLines - GlyphPositions.phraseText.maxLines(), shifted + 3);
}
}
public void rerender() {
setTitle(render());
var slots = getTheme().getPositions().chestSlotsButtons();
var slots = GlyphPositions.chestSlotsButtons;
for (var slot: slots.scrollUp()) {
if (shifted != 0) {
@ -93,7 +93,7 @@ public class DialogueRenderer extends Inventory {
}
for (var slot: slots.scrollDown()) {
if (maxLines > shifted + theme.getPositions().phraseText().maxLines()) {
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
setSlotClickable(slot, null, ClickType.SCROLL_DOWN);
} else {
resetClickableSlot(slot);
@ -267,7 +267,7 @@ public class DialogueRenderer extends Inventory {
}
private Component render() {
var builder = GlyphComponentBuilder.gui(glyphResourcePack.spaces());
var builder = new GlyphComponentBuilder();
// GUI background
builder.append(CHEST_GUI_WIDTH / 2 + 2 - theme.getBackground().part1().width(), theme.getBackground().part1());
@ -276,12 +276,12 @@ public class DialogueRenderer extends Inventory {
builder.append(CHEST_GUI_WIDTH / 2 + 1, theme.getBackground().part4());
// Phrase substrate
builder.append(theme.getPositions().phraseSubstrate().x(), theme.getSubstrate().part1());
builder.append(theme.getPositions().phraseSubstrate().x() + theme.getPositions().phraseSubstrate().width() / 2, theme.getSubstrate().part2());
builder.append(GlyphPositions.phraseSubstrate.x(), theme.getSubstrate().part1());
builder.append(GlyphPositions.phraseSubstrate.x() + GlyphPositions.phraseSubstrate.width() / 2, theme.getSubstrate().part2());
// Frame and avatar
builder.append(theme.getPositions().avatar().x() - 8, theme.getAvatarFrame());
builder.append(theme.getPositions().avatar().x(), theme.getAvatar());
builder.append(GlyphPositions.avatar.x() - 8, theme.getAvatarFrame());
builder.append(GlyphPositions.avatar.x(), theme.getAvatar());
// Phrase text
renderText(builder, shifted);
@ -289,22 +289,24 @@ public class DialogueRenderer extends Inventory {
// Scroll phrase buttons (optional)
if (shifted != 0) {
builder.append(
theme.getPositions().scrollPhraseButton().scrollUpButtonX(),
theme.getScrollTextUp());
GlyphPositions.scrollPhraseButton.scrollUpButtonX(),
theme.getScrollTextUp()
);
}
if (maxLines > shifted + theme.getPositions().phraseText().maxLines()) {
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
builder.append(
theme.getPositions().scrollPhraseButton().scrollDownButtonX(),
theme.getScrollTextDown());
GlyphPositions.scrollPhraseButton.scrollDownButtonX(),
theme.getScrollTextDown()
);
}
// Answers
for (var number: ButtonNumber.values()) {
if (buttonsText.containsKey(number)) {
builder.append(number.getIndex() % 2 == 0
? theme.getPositions().answerField().leftFieldX()
: theme.getPositions().answerField().rightFieldX(),
? GlyphPositions.answerField.leftFieldX()
: GlyphPositions.answerField.rightFieldX(),
switch (number) {
case BUTTON_1 -> theme.getActiveTextField1();
case BUTTON_2 -> theme.getActiveTextField2();
@ -313,8 +315,8 @@ public class DialogueRenderer extends Inventory {
});
builder.append(number.getIndex() % 2 == 0
? theme.getPositions().answerButton().leftButtonX()
: theme.getPositions().answerButton().rightButtonX(),
? GlyphPositions.answerButton.leftButtonX()
: GlyphPositions.answerButton.rightButtonX(),
switch (number) {
case BUTTON_1 -> theme.getButton1();
case BUTTON_2 -> theme.getButton2();
@ -325,19 +327,20 @@ public class DialogueRenderer extends Inventory {
var lines = breakIntoLines(
buttonsText.get(number),
number.getIndex() < 2
? new TextureProperties(theme.getPositions().answerText().fontHeight(), theme.getPositions().answerText().topFirstLineAscent())
: new TextureProperties(theme.getPositions().answerText().fontHeight(), theme.getPositions().answerText().bottomFirstLineAscent()),
theme.getPositions().answerText().fontHeight(),
theme.getPositions().answerText().lineWidth());
? new TextureProperties(GlyphPositions.answerText.fontHeight(), GlyphPositions.answerText.topFirstLineAscent())
: new TextureProperties(GlyphPositions.answerText.fontHeight(), GlyphPositions.answerText.bottomFirstLineAscent()),
GlyphPositions.answerText.fontHeight(),
GlyphPositions.answerText.lineWidth());
for (int lineIdx = 0; lineIdx < Math.min(lines.size(), theme.getPositions().answerText().maxLines()); lineIdx++) {
boolean endWithDots = lineIdx + 1 == theme.getPositions().answerText().maxLines() && lineIdx + 1 != lines.size();
for (int lineIdx = 0; lineIdx < Math.min(lines.size(), GlyphPositions.answerText.maxLines()); lineIdx++) {
boolean endWithDots = lineIdx + 1 == GlyphPositions.answerText.maxLines() && lineIdx + 1 != lines.size();
var line = lines.get(lineIdx);
builder.append(
number.getIndex() % 2 == 0
? theme.getPositions().answerText().leftLineX()
: theme.getPositions().answerText().rightLineX(),
? GlyphPositions.answerText.leftLineX()
: GlyphPositions.answerText.rightLineX(),
line.toGlyphList(line.textureProperties(), 0, endWithDots, switch (number) {
case BUTTON_1 -> theme.getColorAnswerText1();
case BUTTON_2 -> theme.getColorAnswerText2();
@ -346,10 +349,12 @@ public class DialogueRenderer extends Inventory {
})
);
}
} else {
builder.append(number.getIndex() % 2 == 0
? theme.getPositions().answerField().leftFieldX()
: theme.getPositions().answerField().rightFieldX(),
? GlyphPositions.answerField.leftFieldX()
: GlyphPositions.answerField.rightFieldX(),
switch (number) {
case BUTTON_1 -> theme.getNotActiveTextField1();
case BUTTON_2 -> theme.getNotActiveTextField2();
@ -362,13 +367,12 @@ public class DialogueRenderer extends Inventory {
return builder.build();
}
private List<TextLine> breakIntoLines(String input, TextureProperties firstLineProperties, int fontHeight, int maxWidth) throws IllegalStateException {
var resolvedText = MiniMessage.miniMessage().deserialize(input);
var lines = StringUtil.splitIntoParts(resolvedText, maxWidth, line -> {
int width = 0;
for (var glyph: theme.getFont().translate(firstLineProperties.height(), firstLineProperties.ascent(), line)) {
width += glyph.width();
}
width += MinecraftFont.translate(firstLineProperties.height(), firstLineProperties.ascent(), line).width();
return width;
});
@ -381,27 +385,25 @@ public class DialogueRenderer extends Inventory {
var text = lines[lineIdx];
var properties = new TextureProperties(firstLineProperties.height(), firstLineProperties.ascent() - lineIdx * (fontHeight + 1));
textLines.add(new TextLine(theme, text, properties));
textLines.add(new TextLine(theme, ((TextComponent) text).content(), properties));
}
return textLines;
}
private void renderText(GlyphComponentBuilder builder, int shift) {
var positions = DialogGlyphPositions.DEFAULT;
var textLines = breakIntoLines(
text,
positions.phraseText().firstLineProperties(),
positions.phraseText().fontHeight(),
positions.phraseText().lineWidth());
GlyphPositions.phraseText.firstLineProperties(),
GlyphPositions.phraseText.fontHeight(),
GlyphPositions.phraseText.lineWidth());
maxLines = textLines.size();
for (int lineIdx = shift; (lineIdx - shift) < Math.min(textLines.size() - shift, positions.phraseText().maxLines()); lineIdx++) {
for (int lineIdx = shift; (lineIdx - shift) < Math.min(textLines.size() - shift, GlyphPositions.phraseText.maxLines()); lineIdx++) {
var line = textLines.get(lineIdx);
builder.append(positions.phraseText().lineX(), line.toGlyphList(
builder.append(GlyphPositions.phraseText.lineX(), line.toGlyphList(
line.textureProperties(),
shift,
textLines.size() - lineIdx > 0 && lineIdx - shift == positions.phraseText().maxLines() - 1,
textLines.size() - lineIdx > 0 && lineIdx - shift == GlyphPositions.phraseText.maxLines() - 1,
getTheme().getColorText()
));
}
@ -436,23 +438,22 @@ public class DialogueRenderer extends Inventory {
onAnswerClick.put(buttonNumber, onClick);
}
private record TextLine(DialogueTheme theme, Component text, TextureProperties textureProperties) {
private record TextLine(DialogueTheme theme, String text, TextureProperties textureProperties) {
public List<@NotNull AppendableGlyph> toGlyphList(TextureProperties parentProperties, int lineShift, boolean cutEnding, TextColor color) {
public GlyphComponent toGlyphList(TextureProperties parentProperties, int lineShift, boolean cutEnding, TextColor color) {
var properties = new TextureProperties(
parentProperties.height(),
parentProperties.ascent() + lineShift * (textureProperties().height() + 1)
);
Component text = this.text;
Component text = Component.text(this.text);
if (cutEnding) text = StringUtil.cutEnding(text, " <...>");
text = text.applyFallbackStyle(Style.style(color));
return toList((TextComponent) text, properties);
return toList(((TextComponent) text).content(), properties);
}
private List<AppendableGlyph> toList(TextComponent text, TextureProperties properties) {
return theme.getFont().translate(properties.height(), properties.ascent(), text);
private MinecraftFont.Text toList(String text, TextureProperties properties) {
return MinecraftFont.translate(properties.height(), properties.ascent(), text);
}
}

View File

@ -11,11 +11,9 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.SoundEvent;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection;
import ru.dragonestia.msb3.api.resource.DialogueResources;
import ru.dragonestia.msb3.api.resource.dialog.*;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
import java.util.function.Consumer;
@ -24,28 +22,24 @@ import java.util.function.Consumer;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class DialogueTheme {
private final DialogueResources resources = ServerBootstrap.getInstance().getResourcePackManager().getDialogueResources();
private final DialogGlyphPositions positions;
private final Background background;
private final Substrate substrate;
private final ImageGlyph avatar;
private final ImageGlyph avatarFrame;
private final ImageGlyph scrollTextUp;
private final ImageGlyph scrollTextDown;
private final ImageGlyph activeTextField1;
private final ImageGlyph activeTextField2;
private final ImageGlyph activeTextField3;
private final ImageGlyph activeTextField4;
private final ImageGlyph notActiveTextField1;
private final ImageGlyph notActiveTextField2;
private final ImageGlyph notActiveTextField3;
private final ImageGlyph notActiveTextField4;
private final ImageGlyph button1;
private final ImageGlyph button2;
private final ImageGlyph button3;
private final ImageGlyph button4;
private final LanguageGlyphCollection font;
private final GlyphImage avatar;
private final GlyphImage avatarFrame;
private final GlyphImage scrollTextUp;
private final GlyphImage scrollTextDown;
private final GlyphImage activeTextField1;
private final GlyphImage activeTextField2;
private final GlyphImage activeTextField3;
private final GlyphImage activeTextField4;
private final GlyphImage notActiveTextField1;
private final GlyphImage notActiveTextField2;
private final GlyphImage notActiveTextField3;
private final GlyphImage notActiveTextField4;
private final GlyphImage button1;
private final GlyphImage button2;
private final GlyphImage button3;
private final GlyphImage button4;
private final Component textScrollUp;
private final Component textScrollDown;
private final Component textPrefixAnswer;
@ -59,8 +53,6 @@ public class DialogueTheme {
public Builder toBuilder() {
return new Builder(
resources,
positions,
background,
substrate,
avatar,
@ -79,7 +71,6 @@ public class DialogueTheme {
button2,
button3,
button4,
font,
textScrollUp,
textScrollDown,
textPrefixAnswer,
@ -94,38 +85,30 @@ public class DialogueTheme {
}
public static Builder builder() {
return builder(DialogGlyphPositions.DEFAULT);
}
public static Builder builder(DialogGlyphPositions positions) {
return new Builder(positions);
return new Builder();
}
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class Builder {
@Getter private final DialogueResources resources;
private final DialogGlyphPositions positions;
private Background background;
private Substrate substrate;
private ImageGlyph avatar;
private ImageGlyph avatarFrame;
private ImageGlyph scrollTextUp;
private ImageGlyph scrollTextDown;
private ImageGlyph activeTextField1;
private ImageGlyph activeTextField2;
private ImageGlyph activeTextField3;
private ImageGlyph activeTextField4;
private ImageGlyph notActiveTextField1;
private ImageGlyph notActiveTextField2;
private ImageGlyph notActiveTextField3;
private ImageGlyph notActiveTextField4;
private ImageGlyph button1;
private ImageGlyph button2;
private ImageGlyph button3;
private ImageGlyph button4;
private LanguageGlyphCollection font;
private GlyphImage avatar;
private GlyphImage avatarFrame;
private GlyphImage scrollTextUp;
private GlyphImage scrollTextDown;
private GlyphImage activeTextField1;
private GlyphImage activeTextField2;
private GlyphImage activeTextField3;
private GlyphImage activeTextField4;
private GlyphImage notActiveTextField1;
private GlyphImage notActiveTextField2;
private GlyphImage notActiveTextField3;
private GlyphImage notActiveTextField4;
private GlyphImage button1;
private GlyphImage button2;
private GlyphImage button3;
private GlyphImage button4;
private Component textScrollUp;
private Component textScrollDown;
private Component textPrefixAnswer;
@ -137,31 +120,27 @@ public class DialogueTheme {
private TextColor colorAnswerText4;
private Consumer<Player> onScrollText;
private Builder(DialogGlyphPositions positions) {
resources = ServerBootstrap.getInstance().getResourcePackManager().getDialogueResources();
this.positions = positions;
}
private Builder() {}
public DialogueTheme build() {
if (background == null) setBackground(resources.getBackground(DialogueResources.DEFAULT).orElseThrow());
if (substrate == null) setSubstrate(resources.getSubstrate(DialogueResources.DEFAULT).orElseThrow());
if (avatar == null) setAvatar(resources.getAvatar(DialogueResources.DEFAULT).orElseThrow());
if (avatarFrame == null) setAvatarFrame(resources.getAvatarFrame(DialogueResources.DEFAULT).orElseThrow());
if (scrollTextUp == null) setScrollTextUp(resources.getScrollUp(DialogueResources.DEFAULT).orElseThrow());
if (scrollTextDown == null) setScrollTextDown(resources.getScrollDown(DialogueResources.DEFAULT).orElseThrow());
if (font == null) setFont(resources.getFont(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField1 == null) setActiveTextField(ButtonNumber.BUTTON_1, resources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField2 == null) setActiveTextField(ButtonNumber.BUTTON_2, resources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField3 == null) setActiveTextField(ButtonNumber.BUTTON_3, resources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField4 == null) setActiveTextField(ButtonNumber.BUTTON_4, resources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField1 == null) setNotActiveTextField(ButtonNumber.BUTTON_1, resources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField2 == null) setNotActiveTextField(ButtonNumber.BUTTON_2, resources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField3 == null) setNotActiveTextField(ButtonNumber.BUTTON_3, resources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField4 == null) setNotActiveTextField(ButtonNumber.BUTTON_4, resources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (button1 == null) setButton(ButtonNumber.BUTTON_1, resources.getButton(DialogueResources.DEFAULT + 1).orElseThrow());
if (button2 == null) setButton(ButtonNumber.BUTTON_2, resources.getButton(DialogueResources.DEFAULT + 2).orElseThrow());
if (button3 == null) setButton(ButtonNumber.BUTTON_3, resources.getButton(DialogueResources.DEFAULT + 3).orElseThrow());
if (button4 == null) setButton(ButtonNumber.BUTTON_4, resources.getButton(DialogueResources.DEFAULT + 4).orElseThrow());
if (background == null) setBackground(DialogueResources.getBackground(DialogueResources.DEFAULT).orElseThrow());
if (substrate == null) setSubstrate(DialogueResources.getSubstrate(DialogueResources.DEFAULT).orElseThrow());
if (avatar == null) setAvatar(DialogueResources.getAvatar(DialogueResources.DEFAULT).orElseThrow());
if (avatarFrame == null) setAvatarFrame(DialogueResources.getAvatarFrame(DialogueResources.DEFAULT).orElseThrow());
if (scrollTextUp == null) setScrollTextUp(DialogueResources.getScrollUp(DialogueResources.DEFAULT).orElseThrow());
if (scrollTextDown == null) setScrollTextDown(DialogueResources.getScrollDown(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField1 == null) setActiveTextField(ButtonNumber.BUTTON_1, DialogueResources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField2 == null) setActiveTextField(ButtonNumber.BUTTON_2, DialogueResources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField3 == null) setActiveTextField(ButtonNumber.BUTTON_3, DialogueResources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (activeTextField4 == null) setActiveTextField(ButtonNumber.BUTTON_4, DialogueResources.getActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField1 == null) setNotActiveTextField(ButtonNumber.BUTTON_1, DialogueResources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField2 == null) setNotActiveTextField(ButtonNumber.BUTTON_2, DialogueResources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField3 == null) setNotActiveTextField(ButtonNumber.BUTTON_3, DialogueResources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (notActiveTextField4 == null) setNotActiveTextField(ButtonNumber.BUTTON_4, DialogueResources.getNotActiveTextField(DialogueResources.DEFAULT).orElseThrow());
if (button1 == null) setButton(ButtonNumber.BUTTON_1, DialogueResources.getButton(DialogueResources.DEFAULT + 1).orElseThrow());
if (button2 == null) setButton(ButtonNumber.BUTTON_2, DialogueResources.getButton(DialogueResources.DEFAULT + 2).orElseThrow());
if (button3 == null) setButton(ButtonNumber.BUTTON_3, DialogueResources.getButton(DialogueResources.DEFAULT + 3).orElseThrow());
if (button4 == null) setButton(ButtonNumber.BUTTON_4, DialogueResources.getButton(DialogueResources.DEFAULT + 4).orElseThrow());
if (textScrollUp == null) setTextScrollUp(
Component.text()
.append(Component.text("Нажмите сюда чтобы пролистать", NamedTextColor.YELLOW))
@ -195,7 +174,6 @@ public class DialogueTheme {
}
return new DialogueTheme(
positions,
background,
substrate,
avatar,
@ -214,7 +192,6 @@ public class DialogueTheme {
button2,
button3,
button4,
font,
textScrollUp,
textScrollDown,
textPrefixAnswer,
@ -238,31 +215,26 @@ public class DialogueTheme {
return this;
}
public Builder setAvatar(ImageGlyph avatar) {
public Builder setAvatar(GlyphImage avatar) {
this.avatar = avatar;
return this;
}
public Builder setAvatarFrame(ImageGlyph avatarFrame) {
public Builder setAvatarFrame(GlyphImage avatarFrame) {
this.avatarFrame = avatarFrame;
return this;
}
public Builder setScrollTextUp(ImageGlyph scrollTextUp) {
public Builder setScrollTextUp(GlyphImage scrollTextUp) {
this.scrollTextUp = scrollTextUp;
return this;
}
public Builder setScrollTextDown(ImageGlyph scrollTextDown) {
public Builder setScrollTextDown(GlyphImage scrollTextDown) {
this.scrollTextDown = scrollTextDown;
return this;
}
public Builder setFont(LanguageGlyphCollection font) {
this.font = font;
return this;
}
public Builder setActiveTextField(ButtonNumber number, TextField field) {
switch (number) {
case BUTTON_1 -> activeTextField1 = field.get(number);

View File

@ -4,9 +4,9 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.glyph.GlyphComponentBuilder;
import ru.dragonestia.msb3.api.resource.MonologueResources;
import ru.dragonestia.msb3.api.util.StringUtil;
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
import java.util.Objects;
@ -37,8 +37,7 @@ public final class Monologue {
}
private Component toComponent(MonologueTheme theme) {
var spaces = ServerBootstrap.getInstance().getResourcePackManager().getGlyphResourcePack().spaces();
var speech = ServerBootstrap.getInstance().getResourcePackManager().getMonologueResources().getSpeechIndicator();
var speech = MonologueResources.getSpeechIndicator();
Component text = Component.text(message);
Component[] parts = StringUtil.splitIntoParts(text, 180, string -> string.length() * 4);
@ -46,13 +45,13 @@ public final class Monologue {
int avatarLineStart = Math.max(parts.length - 4, 0) / 2;
if (parts.length > 3) ++avatarLineStart;
var avatarComponent = GlyphComponentBuilder.universal(spaces)
var avatarComponent = new GlyphComponentBuilder()
.append(Objects.requireNonNull(theme.getFrame()))
.append(4, Objects.requireNonNull(theme.getAvatar()))
.build();
var titleComponent = Component.empty()
.append(speech.toAdventure())
.append(speech.component())
.append(Component.space()).append(Component.text(title, theme.getTitleColor(), TextDecoration.BOLD));
var monolog = Component.text();

View File

@ -6,10 +6,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
import ru.dragonestia.msb3.api.resource.MonologueResources;
import ru.dragonestia.msb3.api.resource.ResourcePackManager;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
import java.util.Objects;
@ -18,14 +16,13 @@ import java.util.Objects;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class MonologueTheme {
private final ResourcePackManager resourcePackManager = ServerBootstrap.getInstance().getResourcePackManager();
private final ImageGlyph avatar;
private final ImageGlyph frame;
private final GlyphImage avatar;
private final GlyphImage frame;
private final TextColor titleColor;
private final TextColor textColor;
public MonologueTheme withAvatar(String identifier) {
var opt = resourcePackManager.getMonologueResources().getAvatar(identifier);
var opt = MonologueResources.getAvatar(identifier);
if (opt.isPresent()) {
return withAvatar(opt.get());
}
@ -35,7 +32,7 @@ public class MonologueTheme {
return this;
}
public MonologueTheme withAvatar(ImageGlyph avatar) {
public MonologueTheme withAvatar(GlyphImage avatar) {
return new MonologueTheme(avatar, frame, titleColor, textColor);
}
@ -45,16 +42,12 @@ public class MonologueTheme {
public static class Builder {
private final ResourcePackManager resourcePackManager;
private ImageGlyph avatar = null;
private ImageGlyph frame = null;
private GlyphImage avatar = null;
private GlyphImage frame = null;
private TextColor titleColor = null;
private TextColor textColor = null;
private Builder() {
resourcePackManager = ServerBootstrap.getInstance().getResourcePackManager();
}
private Builder() {}
public MonologueTheme build() {
if (avatar == null) {
@ -80,18 +73,18 @@ public class MonologueTheme {
);
}
public Builder setAvatar(ImageGlyph avatar) {
public Builder setAvatar(GlyphImage avatar) {
this.avatar = avatar;
return this;
}
public Builder setFrame(ImageGlyph frame) {
public Builder setFrame(GlyphImage frame) {
this.frame = frame;
return this;
}
public Builder setAvatar(String identifier) {
var opt = resourcePackManager.getMonologueResources().getAvatar(identifier);
var opt = MonologueResources.getAvatar(identifier);
if (opt.isPresent()) {
avatar = Objects.requireNonNull(opt.get());
return this;
@ -102,7 +95,7 @@ public class MonologueTheme {
}
public Builder setFrame(String identifier) {
var opt = resourcePackManager.getMonologueResources().getFrame(identifier);
var opt = MonologueResources.getFrame(identifier);
if (opt.isPresent()) {
frame = Objects.requireNonNull(opt.get());
return this;

View File

@ -1,35 +1,28 @@
package ru.dragonestia.msb3.api.title;
import lombok.experimental.UtilityClass;
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;
import ru.dragonestia.msb3.resource.Resources;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
@UtilityClass
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 final GlyphImage glyph = Resources.createGlyph(
Key.key("msb3", "black_screen"),
ResourceFromJar.of("glyphs/defaults/fullscreen_background.png"),
2500,
256
);
public static void show(Player player) {
player.showTitle(Title.title(
GLYPH.toAdventure(),
glyph.component(),
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

@ -1,27 +0,0 @@
package ru.dragonestia.msb3.api.util;
import lombok.experimental.UtilityClass;
import team.unnamed.creative.base.Writable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
@UtilityClass
public class ImageUtil {
public Writable imageToWritable(BufferedImage image) {
try {
return imageToWritable0(image);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private Writable imageToWritable0(BufferedImage image) throws Exception {
try (var stream = new ByteArrayOutputStream()) {
ImageIO.write(image, "png", stream);
return Writable.bytes(stream.toByteArray());
}
}
}

View File

@ -1,6 +1,6 @@
package ru.dragonestia.msb3.api.util;
import ru.dragonestia.msb3.api.ServerBootstrap;
import ru.dragonestia.msb3.api.boot.ServerBootstrap;
import team.unnamed.creative.base.Writable;
import java.io.IOException;

View File

@ -1,5 +1,6 @@
package ru.dragonestia.msb3.resource;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.key.Key;
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
@ -18,8 +19,8 @@ import java.util.HashMap;
@UtilityClass
public final class Resources {
private final ResourcePack resourcePack = init();
private final Font.Builder defaultFont = Font.font();
@Getter private final ResourcePack resourcePack = init();
@Getter private final Font.Builder defaultFont = Font.font();
private final GlyphRegistry glyphs = new GlyphRegistry(resourcePack, defaultFont);
private boolean compiled = false;
@ -69,14 +70,15 @@ public final class Resources {
* @param writableTexture Объект с текстурой, предоставляемый ресурс-паку
* @param height Высота глифа
* @param ascent Смещение глифа по высоте
* @return Объект глифа
* @throws IllegalStateException Глиф был создан после компиляции ресурс-пака
*/
public void createGlyph(Key key, Writable writableTexture, int height, int ascent) throws IllegalStateException {
public GlyphImage createGlyph(Key key, Writable writableTexture, int height, int ascent) throws IllegalStateException {
if (compiled) {
throw new IllegalStateException("Resources already compiled");
}
glyphs.register(key, writableTexture, height, ascent);
return glyphs.register(key, writableTexture, height, ascent);
}
/**

View File

@ -1,6 +1,9 @@
package ru.dragonestia.msb3.resource.glyph;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import java.util.ArrayList;
import java.util.List;
@ -74,35 +77,57 @@ public class GlyphComponentBuilder {
* @return Текстовый компонент
*/
public Component build(boolean resetPosition) {
var builder = Component.text();
var sb = new StringBuilder();
var text = buildAsGlyphComponent();
sb.append(text.content());
if (resetPosition) {
sb.append(Spacing.getAsString(-text.width()));
}
var style = Style.style()
.color(NamedTextColor.WHITE)
.decoration(TextDecoration.ITALIC, false)
.decoration(TextDecoration.BOLD, false)
.decoration(TextDecoration.UNDERLINED, false)
.decoration(TextDecoration.STRIKETHROUGH, false)
.decoration(TextDecoration.OBFUSCATED, false)
.build();
return Component.text(sb.toString(), style);
}
/**
* Собрать все компоненты глифов в один компонент глифа
* @return Компонент глифа
*/
public GlyphComponent buildAsGlyphComponent() {
var builder = new StringBuilder();
int offset = 0;
for (var entry: entries) {
Component component = entry.glyphComponent().component();
var val = entry.offset();
switch (entry.position()) {
var component = entry.glyphComponent;
var val = entry.offset;
switch (entry.position) {
case ABSOLUTE -> {
builder.append(Spacing.get(val - offset));
builder.append(Spacing.getAsString(val - offset));
offset = val;
}
case RELATIVE -> {
if (val == 0) {
builder.append(Spacing.get(val));
builder.append(Spacing.getAsString(val));
}
offset += val;
}
}
offset += entry.glyphComponent().width();
builder.append(component);
offset += entry.glyphComponent.width() + 1;
builder.append(component.content());
}
if (resetPosition) {
builder.append(Spacing.get(-offset));
}
return builder.build();
return new Text(builder.toString(), offset);
}
private record Entry(Position position, int offset, GlyphComponent glyphComponent) {}

View File

@ -10,9 +10,7 @@ import team.unnamed.creative.font.Font;
import team.unnamed.creative.font.FontProvider;
import team.unnamed.creative.texture.Texture;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.*;
public class GlyphRegistry {
@ -25,13 +23,16 @@ public class GlyphRegistry {
public GlyphRegistry(ResourcePack resourcePack, Font.Builder defaultFont) {
this.resourcePack = resourcePack;
this.defaultFont = defaultFont;
precompileSpacing();
}
private void precompileSpacing() {
defaultFont.key(Font.MINECRAFT_DEFAULT);
defaultFont.addProvider(FontProvider.reference(Spacing.compile(resourcePack)));
}
public void compile() {
defaultFont.key(Font.MINECRAFT_DEFAULT);
defaultFont.addProvider(FontProvider.reference(Spacing.compile(resourcePack)));
for (var fontBuilder: glyphFonts.values()) {
var font = fontBuilder.build();
@ -44,19 +45,21 @@ public class GlyphRegistry {
public void compileAtlases(Map<String, Atlas.Builder> atlases) {
for (var holder: glyphs.values()) {
var key = holder.texture().key();
for (var texture: holder.textures()) {
var key = texture.key();
var atlas = atlases.computeIfAbsent(key.namespace(), identifier -> {
var obj = Atlas.atlas();
obj.key(Key.key(identifier, "gui"));
return obj;
});
var atlas = atlases.computeIfAbsent(key.namespace(), identifier -> {
var obj = Atlas.atlas();
obj.key(Key.key(identifier, "gui"));
return obj;
});
atlas.addSource(AtlasSource.single(Key.key(key.namespace(), key.value().replace(".png", ""))));
atlas.addSource(AtlasSource.single(Key.key(key.namespace(), key.value().replace(".png", ""))));
}
}
}
public void register(Key key, Writable writableTexture, int height, int ascent) {
public GlyphImage register(Key key, Writable writableTexture, int height, int ascent) {
var font = glyphFonts.computeIfAbsent(key.namespace(), namespace -> {
var obj = Font.font();
obj.key(Key.key(namespace, "glyphs_font"));
@ -65,7 +68,6 @@ public class GlyphRegistry {
var character = GlyphCharacterFactory.takeFreeCharacter();
var image = ImageUtils.imageFromWritable(writableTexture);
// TODO: split image when resolution biggest than 256x256 on any axis
var texture = Texture.texture()
.key(Key.key(key.namespace(), "glyphs/" + key.value() + ".png"))
@ -89,9 +91,10 @@ public class GlyphRegistry {
}
var glyph = new GlyphImage(Character.toString(character), height, wight, ascent);
var holder = new GlyphResourceHolder(key, glyph, texture);
var holder = new GlyphResourceHolder(key, glyph, List.of(texture));
glyphs.put(key, holder);
return holder.glyph();
}
public GlyphImage getGlyph(Key key) {

View File

@ -3,4 +3,6 @@ package ru.dragonestia.msb3.resource.glyph;
import net.kyori.adventure.key.Key;
import team.unnamed.creative.texture.Texture;
public record GlyphResourceHolder(Key key, GlyphImage glyph, Texture texture) {}
import java.util.List;
public record GlyphResourceHolder(Key key, GlyphImage glyph, List<Texture> textures) {}

View File

@ -199,7 +199,7 @@ public final class MinecraftFont {
private Text(String content, int length) {
this.content = content;
this.length = length;
this.length = length -1;
}
@Override

View File

@ -61,7 +61,7 @@ public class Spacing {
var spacingChar = map.get(index);
if (spacingChar == null) {
throw new IllegalArgumentException("Unsupported spacing depth: %s".formatted(currentDepth));
throw new IllegalArgumentException("Unsupported spacing depth: %s (index=%s)".formatted(currentDepth, index));
}
sb.append(spacingChar);

View File

@ -0,0 +1,38 @@
package ru.dragonestia.msb3.resource.utils;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ClassPreLoader {
/**
* Предварительная загрузка класса. Сделано для того, чтобы инициализировать статические поля классов.
*
* <p>Реализация решения проблемы, когда регистрация глифа происходит в статическом финальном поле класса.
* Java не загружает все статические поля при старте программы, а только в тот момент, когда идет первое
* обращение к классу.
*
* <p> Пример:
* <blockquote><pre>
* class BlackScreen {
* public static final GlyphImage GLYPH = Resources.createGlyph(
* Key.key("msb3", "black_screen"),
* ResourceFromJar.of("glyphs/defaults/fullscreen_background.png"),
* 2500,
* 256
* );
* }
*
* // Место, где инициализируются глифы
* void initializeGlyphs() {
* ClassPreLoader.preload(BlackScreen.class);
* }
* </pre></blockquote>
* @param clazz Предварительно загружаемый класс
*/
public void preload(Class<?> clazz) {
try {
Class.forName(clazz.getName());
} catch (Exception ignored) {}
}
}

View File

@ -6,6 +6,7 @@ import team.unnamed.creative.base.Writable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@UtilityClass
@ -18,4 +19,19 @@ public class ImageUtils {
throw new RuntimeException(ex);
}
}
public Writable imageToWritable(BufferedImage image) {
try {
return imageToWritable0(image);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private Writable imageToWritable0(BufferedImage image) throws Exception {
try (var stream = new ByteArrayOutputStream()) {
ImageIO.write(image, "png", stream);
return Writable.bytes(stream.toByteArray());
}
}
}