diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index 7ddd21f54..8d5ad85c2 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -53,4 +53,14 @@ command.search_recipe.not_found: "No recipe found for this item" command.search_usage.not_found: "No usage found for this item" command.search_recipe.no_item: "Please hold an item before running this command" command.search_usage.no_item: "Please hold an item before running this command" -command.totem.not_totem: "'' is not type of totem_of_undying" \ No newline at end of file +command.totem.not_totem: "'' is not type of totem_of_undying" + +warning.config.image.lack_height: "Issue found in file - The image '' is missing the required 'height' argument." +warning.config.image.height_smaller_than_ascent: "Issue found in file - The image '' violates the bitmap image rule: 'height' should be no lower than 'ascent'." +warning.config.image.no_file: "Issue found in file - The image '' is missing the required 'file' argument." +warning.config.image.invalid_resource_location: "Issue found in file - The image '' has a 'file' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" +warning.config.image.invalid_font_name: "Issue found in file - The image '' has a 'font' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" +warning.config.image.lack_char: "Issue found in file - The image '' is missing the required 'char' argument." +warning.config.image.codepoint_in_use: "Issue found in file - The image '' is using a character[()] in font that has been used by another image ''." +warning.config.image.invalid_codepoint_grid: "Issue found in file - Image '' has an invalid 'chars' codepoint grind." +warning.config.image.file_not_exist: "Issue found in file - PNG file not found for image ''." \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java index 3618b8d43..61d50a512 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineBlocks.java @@ -6,7 +6,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; @@ -14,7 +13,6 @@ import net.momirealms.craftengine.core.block.UpdateOption; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.parameter.LootParameters; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.Vec3d; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 3bc68b877..353cd94a8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.ImmutableBlockState; -import net.momirealms.craftengine.core.block.PushReaction; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.item.Item; @@ -29,7 +28,10 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.*; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.world.GenericGameEvent; import org.bukkit.inventory.ItemStack; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java index cb2651ba5..979d8aff0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/CropBlockBehavior.java @@ -13,14 +13,13 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.IntegerProperty; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.loot.LootContext; +import net.momirealms.craftengine.core.loot.number.NumberProvider; +import net.momirealms.craftengine.core.loot.number.NumberProviders; import net.momirealms.craftengine.core.loot.parameter.LootParameters; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.RandomUtils; import net.momirealms.craftengine.core.util.Tuple; -import net.momirealms.craftengine.core.loot.number.NumberProvider; -import net.momirealms.craftengine.core.loot.number.NumberProviders; import net.momirealms.craftengine.core.util.context.ContextHolder; -import net.momirealms.craftengine.core.util.context.ContextKey; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3i; import net.momirealms.craftengine.shared.block.BlockBehavior; @@ -29,7 +28,6 @@ import org.bukkit.World; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ThreadLocalRandom; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java similarity index 90% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 6dcd6e430..3f8056e54 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitImageManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -4,8 +4,8 @@ import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.font.AbstractImageManager; -import net.momirealms.craftengine.core.font.ImageManager; +import net.momirealms.craftengine.core.font.AbstractFontManager; +import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.util.CharacterUtils; import org.bukkit.Bukkit; @@ -19,11 +19,11 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent; import java.lang.reflect.InvocationTargetException; import java.util.function.Consumer; -public class BukkitImageManager extends AbstractImageManager implements Listener { +public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; private final Object serializer; - public BukkitImageManager(BukkitCraftEngine plugin) { + public BukkitFontManager(BukkitCraftEngine plugin) { super(plugin); this.plugin = plugin; try { @@ -63,7 +63,7 @@ public class BukkitImageManager extends AbstractImageManager implements Listener public void onCommand(PlayerCommandPreprocessEvent event) { if (!ConfigManager.filterCommand()) return; if (!this.isDefaultFontInUse()) return; - if (event.getPlayer().hasPermission(ImageManager.BYPASS_COMMAND)) { + if (event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { return; } runIfContainsIllegalCharacter(event.getMessage(), event::setMessage); @@ -74,7 +74,7 @@ public class BukkitImageManager extends AbstractImageManager implements Listener Player player = event.player(); if (player == null) return; if (!this.isDefaultFontInUse()) return; - if (player.hasPermission(ImageManager.BYPASS_CHAT)) { + if (player.hasPermission(FontManager.BYPASS_CHAT)) { return; } try { @@ -101,7 +101,7 @@ public class BukkitImageManager extends AbstractImageManager implements Listener boolean hasIllegal = false; for (int i = 0; i < codepoints.length; i++) { int codepoint = codepoints[i]; - if (!isIllegalCharacter(codepoint)) { + if (!isIllegalCodepoint(codepoint)) { newCodepoints[i] = codepoint; } else { newCodepoints[i] = '*'; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index 29c2b98dc..9b755b212 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -7,7 +7,7 @@ import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; -import net.momirealms.craftengine.bukkit.font.BukkitImageManager; +import net.momirealms.craftengine.bukkit.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors; import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; @@ -154,7 +154,7 @@ public class BukkitCraftEngine extends CraftEngine { super.worldManager = new BukkitWorldManager(this); super.soundManager = new BukkitSoundManager(this); super.vanillaLootManager = new BukkitVanillaLootManager(this); - this.imageManager = new BukkitImageManager(this); + this.fontManager = new BukkitFontManager(this); super.enable(); // tick task if (VersionHelper.isFolia()) { @@ -216,7 +216,7 @@ public class BukkitCraftEngine extends CraftEngine { // register template parser this.packManager.registerConfigSectionParser(this.templateManager); // register font parser - this.packManager.registerConfigSectionParser(this.imageManager); + this.packManager.registerConfigSectionParsers(this.fontManager.parsers()); // register item parser this.packManager.registerConfigSectionParser(this.itemManager); // register furniture parser diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java index 57985118a..df890ee5d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitSenderFactory.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.core.plugin.command.sender.Sender; import net.momirealms.craftengine.core.plugin.command.sender.SenderFactory; import net.momirealms.craftengine.core.util.Tristate; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.RemoteConsoleCommandSender; @@ -79,6 +80,12 @@ public class BukkitSenderFactory extends SenderFactory C consoleCommandSender() { + return (C) Bukkit.getConsoleSender(); + } + @Override public void close() { super.close(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 78145a505..f7df2d8ab 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -18,7 +18,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; -import net.momirealms.craftengine.core.font.ImageManager; +import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigManager; import net.momirealms.craftengine.core.plugin.network.ConnectionState; @@ -777,10 +777,10 @@ public class PacketConsumers { if (!ConfigManager.filterAnvil()) return; String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet); if (message != null && !message.isEmpty()) { - ImageManager manager = CraftEngine.instance().imageManager(); + FontManager manager = CraftEngine.instance().imageManager(); if (!manager.isDefaultFontInUse()) return; // check bypass - if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_ANVIL)) { + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { return; } runIfContainsIllegalCharacter(message, manager, (s) -> { @@ -801,10 +801,10 @@ public class PacketConsumers { try { if (!ConfigManager.filterSign()) return; String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); - ImageManager manager = CraftEngine.instance().imageManager(); + FontManager manager = CraftEngine.instance().imageManager(); if (!manager.isDefaultFontInUse()) return; // check bypass - if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_SIGN)) { + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { return; } for (int i = 0; i < lines.length; i++) { @@ -827,10 +827,10 @@ public class PacketConsumers { public static final TriConsumer EDIT_BOOK = (user, event, packet) -> { try { if (!ConfigManager.filterBook()) return; - ImageManager manager = CraftEngine.instance().imageManager(); + FontManager manager = CraftEngine.instance().imageManager(); if (!manager.isDefaultFontInUse()) return; // check bypass - if (((BukkitServerPlayer) user).hasPermission(ImageManager.BYPASS_BOOK)) { + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_BOOK)) { return; } @@ -879,7 +879,7 @@ public class PacketConsumers { } }; - private static Pair processClientString(String original, ImageManager manager) { + private static Pair processClientString(String original, FontManager manager) { if (original.isEmpty()) { return Pair.of(false, original); } @@ -888,7 +888,7 @@ public class PacketConsumers { boolean hasIllegal = false; for (int i = 0; i < codepoints.length; i++) { int codepoint = codepoints[i]; - if (manager.isIllegalCharacter(codepoint)) { + if (manager.isIllegalCodepoint(codepoint)) { newCodepoints[i] = '*'; hasIllegal = true; } else { @@ -898,14 +898,14 @@ public class PacketConsumers { return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); } - private static void runIfContainsIllegalCharacter(String string, ImageManager manager, Consumer callback) { + private static void runIfContainsIllegalCharacter(String string, FontManager manager, Consumer callback) { if (string.isEmpty()) return; int[] codepoints = CharacterUtils.charsToCodePoints(string.toCharArray()); int[] newCodepoints = new int[codepoints.length]; boolean hasIllegal = false; for (int i = 0; i < codepoints.length; i++) { int codepoint = codepoints[i]; - if (!manager.isIllegalCharacter(codepoint)) { + if (!manager.isIllegalCodepoint(codepoint)) { newCodepoints[i] = codepoint; } else { newCodepoints[i] = '*'; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java new file mode 100644 index 000000000..fdd4f93a5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -0,0 +1,243 @@ +package net.momirealms.craftengine.core.font; + +import net.momirealms.craftengine.core.pack.LoadingSequence; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.pack.ResourceLocation; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; +import net.momirealms.craftengine.core.util.CharacterUtils; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public abstract class AbstractFontManager implements FontManager { + private final CraftEngine plugin; + // namespace:font font + private final Map fonts = new HashMap<>(); + // namespace:id image + private final Map images = new HashMap<>(); + private final Set illegalChars = new HashSet<>(); + private final ImageParser imageParser; + private final EmojiParser emojiParser; + + private OffsetFont offsetFont; + + public AbstractFontManager(CraftEngine plugin) { + this.plugin = plugin; + this.imageParser = new ImageParser(); + this.emojiParser = new EmojiParser(); + } + + @Override + public void load() { + this.offsetFont = Optional.ofNullable(plugin.configManager().settings().getSection("offset-characters")) + .map(OffsetFont::new) + .orElse(null); + } + + @Override + public void unload() { + this.fonts.clear(); + this.images.clear(); + this.illegalChars.clear(); + } + + @Override + public ConfigSectionParser[] parsers() { + return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; + } + + @Override + public void delayedLoad() { + Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); + } + + @Override + public boolean isDefaultFontInUse() { + return !this.illegalChars.isEmpty(); + } + + @Override + public boolean isIllegalCodepoint(int codepoint) { + return this.illegalChars.contains(codepoint); + } + + @Override + public Collection fonts() { + return new ArrayList<>(this.fonts.values()); + } + + @Override + public Optional bitmapImageByCodepoint(Key font, int codepoint) { + return fontById(font).map(f -> f.bitmapImageByCodepoint(codepoint)); + } + + @Override + public Optional bitmapImageByImageId(Key id) { + return Optional.ofNullable(this.images.get(id)); + } + + @Override + public int codepointByImageId(Key key, int x, int y) { + BitmapImage image = this.images.get(key); + if (image == null) return -1; + return image.codepointAt(x, y); + } + + @Override + public String createOffsets(int offset, FontTagFormatter tagFormatter) { + return Optional.ofNullable(this.offsetFont).map(it -> it.createOffset(offset, tagFormatter)).orElse(""); + } + + @Override + public Optional fontById(Key id) { + return Optional.ofNullable(this.fonts.get(id)); + } + + private Font getOrCreateFont(Key key) { + return this.fonts.computeIfAbsent(key, Font::new); + } + + public class EmojiParser implements ConfigSectionParser { + public static final String[] CONFIG_SECTION_NAME = new String[] {"emoji", "emojis"}; + + @Override + public String[] sectionId() { + return CONFIG_SECTION_NAME; + } + + @Override + public int loadingSequence() { + return LoadingSequence.EMOJI; + } + + @Override + public void parseSection(Pack pack, Path path, Key id, Map section) { + + } + } + + public class ImageParser implements ConfigSectionParser { + public static final String[] CONFIG_SECTION_NAME = new String[] {"images", "image"}; + + @Override + public String[] sectionId() { + return CONFIG_SECTION_NAME; + } + + @Override + public int loadingSequence() { + return LoadingSequence.IMAGE; + } + + @Override + public void parseSection(Pack pack, Path path, Key id, Map section) { + Object heightObj = section.get("height"); + if (heightObj == null) { + TranslationManager.instance().log("warning.config.image.lack_height", path.toString(), id.toString()); + return; + } + + int height = MiscUtils.getAsInt(heightObj); + int ascent = MiscUtils.getAsInt(section.getOrDefault("ascent", height - 1)); + if (height < ascent) { + TranslationManager.instance().log("warning.config.image.height_smaller_than_ascent", path.toString(), id.toString()); + return; + } + + Object file = section.get("file"); + if (file == null) { + TranslationManager.instance().log("warning.config.image.no_file", path.toString(), id.toString()); + return; + } + + String resourceLocation = file.toString().replace("\\", "/"); + if (!ResourceLocation.isValid(resourceLocation)) { + TranslationManager.instance().log("warning.config.image.invalid_resource_location", path.toString(), id.toString(), resourceLocation); + return; + } + + String fontName = (String) section.getOrDefault("font", "minecraft:default"); + if (!ResourceLocation.isValid(fontName)) { + TranslationManager.instance().log("warning.config.image.invalid_font_name", path.toString(), id.toString(), fontName); + return; + } + + Key fontKey = Key.withDefaultNamespace(fontName, id.namespace()); + Font font = getOrCreateFont(fontKey); + List chars; + if (section.containsKey("chars")) { + chars = MiscUtils.getAsStringList(section.get("chars")).stream().map(it -> { + if (it.startsWith("\\u")) { + return CharacterUtils.decodeUnicodeToChars(it); + } else { + return it.toCharArray(); + } + }).toList(); + } else { + String character = (String) section.get("char"); + if (character == null) { + TranslationManager.instance().log("warning.config.image.lack_char", path.toString(), id.toString()); + return; + } + if (character.length() == 1) { + chars = List.of(character.toCharArray()); + } else { + chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); + } + } + + int size = -1; + int[][] codepointGrid = new int[chars.size()][]; + for (int i = 0; i < chars.size(); ++i) { + int[] codepoints = CharacterUtils.charsToCodePoints(chars.get(i)); + for (int codepoint : codepoints) { + if (font.isCodepointInUse(codepoint)) { + BitmapImage image = font.bitmapImageByCodepoint(codepoint); + TranslationManager.instance().log("warning.config.image.codepoint_in_use", + path.toString(), + id.toString(), + fontKey.toString(), + CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), + new String(Character.toChars(codepoint)), + image.id().toString() + ); + return; + } + } + codepointGrid[i] = codepoints; + if (size == -1) size = codepoints.length; + if (size != codepoints.length) { + TranslationManager.instance().log("warning.config.image.invalid_codepoint_grid", path.toString(), id.toString()); + return; + } + } + + if (!resourceLocation.endsWith(".png")) resourceLocation += ".png"; + Key namespacedPath = Key.of(resourceLocation); + Path targetImagePath = pack.resourcePackFolder() + .resolve("assets") + .resolve(namespacedPath.namespace()) + .resolve("textures") + .resolve(namespacedPath.value()); + + if (!Files.exists(targetImagePath)) { + TranslationManager.instance().log("warning.config.image.file_not_exist", path.toString(), id.toString(), targetImagePath.toString()); + // DO NOT RETURN, JUST GIVE WARNINGS + } + + BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid); + for (int[] y : codepointGrid) { + for (int x : y) { + font.addBitMapImage(x, bitmapImage); + } + } + + AbstractFontManager.this.images.put(id, bitmapImage); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java deleted file mode 100644 index 1d81246b8..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractImageManager.java +++ /dev/null @@ -1,172 +0,0 @@ -package net.momirealms.craftengine.core.font; - -import net.momirealms.craftengine.core.pack.Pack; -import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.CharacterUtils; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.PreConditions; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.BiFunction; - -public abstract class AbstractImageManager implements ImageManager { - private final CraftEngine plugin; - // namespace:font font - private final HashMap fonts = new HashMap<>(); - // namespace:id image - private final HashMap images = new HashMap<>(); - private final Set illegalChars = new HashSet<>(); - - private OffsetFont offsetFont; - - public AbstractImageManager(CraftEngine plugin) { - this.plugin = plugin; - } - - @Override - public void load() { - this.offsetFont = Optional.ofNullable(plugin.configManager().settings().getSection("offset-characters")) - .map(OffsetFont::new) - .orElse(null); - } - - @Override - public void unload() { - this.fonts.clear(); - this.images.clear(); - this.illegalChars.clear(); - } - - @Override - public void delayedLoad() { - Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> { - this.illegalChars.addAll(font.codepointsInUse()); - }); - } - - @Override - public boolean isDefaultFontInUse() { - return !this.illegalChars.isEmpty(); - } - - @Override - public boolean isIllegalCharacter(int codepoint) { - return this.illegalChars.contains(codepoint); - } - - @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - int height = MiscUtils.getAsInt(section.get("height")); - int ascent = MiscUtils.getAsInt(section.get("ascent")); - if (PreConditions.runIfTrue(height < ascent, - () -> this.plugin.logger().warn(path, "Illegal ascent found at " + id + ". Height should be no lower than ascent"))) return; - - String file = (String) section.get("file"); - if (PreConditions.isNull(file, - () -> this.plugin.logger().warn(path, "`file` option is not set in image " + id))) return; - - String fontName = (String) section.getOrDefault("font", "minecraft:default"); - if (PreConditions.isNull(fontName, - () -> this.plugin.logger().warn(path, "`font` option is not set in image " + id))) return; - - Key fontKey = Key.withDefaultNamespace(fontName, id.namespace()); - // get the font - Font font = this.getOrCreateFont(fontKey); - List chars; - if (section.containsKey("chars")) { - chars = MiscUtils.getAsStringList(section.get("chars")).stream().map(it -> { - if (it.startsWith("\\u")) { - return CharacterUtils.decodeUnicodeToChars(it); - } else { - return it.toCharArray(); - } - }).toList(); - } else { - String character = (String) section.get("char"); - if (PreConditions.isNull(character, - () -> this.plugin.logger().warn(path, "`char` option is not set in image " + id))) return; - if (character.length() == 1) { - chars = List.of(character.toCharArray()); - } else { - chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); - } - } - - int size = -1; - int[][] codepointGrid = new int[chars.size()][]; - for (int i = 0; i < chars.size(); ++i) { - int[] codepoints = CharacterUtils.charsToCodePoints(chars.get(i)); - for (int codepoint : codepoints) { - if (PreConditions.runIfTrue(font.isCodepointInUse(codepoint), - () -> this.plugin.logger().warn(path, String.format("Codepoint [%s (%s)] is already used in font [%s]", CharacterUtils.encodeCharsToUnicode(Character.toChars(codepoint)), new String(Character.toChars(codepoint)), font.key().toString())))) return; - } - codepointGrid[i] = codepoints; - if (size == -1) size = codepoints.length; - if (PreConditions.runIfTrue(size != codepoints.length, - () -> this.plugin.logger().warn(path, "Illegal chars format found at " + id))) return; - } - - if (PreConditions.runIfTrue(size == -1, - () -> this.plugin.logger().warn(path, "Illegal chars format found at " + id))) return; - - if (!file.endsWith(".png")) file += ".png"; - file = file.replace("\\", "/"); - Key namespacedPath = Key.of(file); - Path targetImageFile = pack.resourcePackFolder() - .resolve("assets") - .resolve(namespacedPath.namespace()) - .resolve("textures") - .resolve(namespacedPath.value()); - - if (PreConditions.runIfTrue(!Files.exists(targetImageFile), - () -> this.plugin.logger().warn(targetImageFile, "PNG file not found for image " + id))) return; - - BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, file, codepointGrid); - for (int[] y : codepointGrid) { - for (int x : y) { - font.registerCodepoint(x, bitmapImage); - } - } - - this.images.put(id, bitmapImage); - } - - @Override - public Collection fontsInUse() { - return new ArrayList<>(this.fonts.values()); - } - - @Override - public Optional bitmapImageByCodepoint(Key font, int codepoint) { - return getFontInUse(font).map(f -> f.getImageByCodepoint(codepoint)); - } - - @Override - public Optional bitmapImageByImageId(Key id) { - return Optional.ofNullable(this.images.get(id)); - } - - @Override - public int codepointByImageId(Key key, int x, int y) { - BitmapImage image = this.images.get(key); - if (image == null) return -1; - return image.codepointAt(x, y); - } - - @Override - public String createOffsets(int offset, BiFunction tagFormatter) { - return Optional.ofNullable(this.offsetFont).map(it -> it.createOffset(offset, tagFormatter)).orElse(""); - } - - @Override - public Optional getFontInUse(Key key) { - return Optional.ofNullable(fonts.get(key)); - } - - private Font getOrCreateFont(Key key) { - return this.fonts.computeIfAbsent(key, Font::new); - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java b/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java index bc96bd66b..b7ec00095 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/BitmapImage.java @@ -7,15 +7,15 @@ import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.Key; public class BitmapImage implements FontProvider { - private final Key imageId; + private final Key id; private final Key font; private final int height; private final int ascent; private final String file; private final int[][] codepointGrid; - public BitmapImage(Key imageId, Key font, int height, int ascent, String file, int[][] codepointGrid) { - this.imageId = imageId; + public BitmapImage(Key id, Key font, int height, int ascent, String file, int[][] codepointGrid) { + this.id = id; this.font = font; this.height = height; this.ascent = ascent; @@ -39,16 +39,16 @@ public class BitmapImage implements FontProvider { return font; } - public Key imageId() { - return imageId; + public Key id() { + return id; } public int[][] codepointGrid() { - return codepointGrid; + return codepointGrid.clone(); } public int codepointAt(int row, int column) { - if (row < 0 || row >= codepointGrid.length || column < 0 || column >= codepointGrid[row].length) { + if (!isValidCoordinate(row, column)) { throw new IndexOutOfBoundsException("Invalid index: (" + row + ", " + column + ")"); } return codepointGrid[row][column]; @@ -69,16 +69,16 @@ public class BitmapImage implements FontProvider { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; BitmapImage image = (BitmapImage) object; - return imageId.equals(image.imageId); + return id.equals(image.id); } @Override public int hashCode() { - return imageId.hashCode(); + return id.hashCode(); } @Override - public JsonObject getJson() { + public JsonObject get() { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("type", "bitmap"); jsonObject.addProperty("height", height); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java index eb6ebc802..cfc1cfab9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java @@ -4,12 +4,12 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.Map; public class Font { private final Key key; - private final HashMap idToCodepoint = new LinkedHashMap<>(); + private final Map idToCodepoint = new LinkedHashMap<>(); public Font(Key key) { this.key = key; @@ -24,11 +24,11 @@ public class Font { return Collections.unmodifiableCollection(this.idToCodepoint.keySet()); } - public BitmapImage getImageByCodepoint(int codepoint) { + public BitmapImage bitmapImageByCodepoint(int codepoint) { return this.idToCodepoint.get(codepoint); } - public void registerCodepoint(int codepoint, BitmapImage image) { + public void addBitMapImage(int codepoint, BitmapImage image) { this.idToCodepoint.put(codepoint, image); } @@ -37,7 +37,7 @@ public class Font { } public Collection bitmapImages() { - return idToCodepoint.values().stream().distinct().toList(); + return this.idToCodepoint.values().stream().distinct().toList(); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java similarity index 65% rename from core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java rename to core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index cf398ddf2..345f63f29 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/ImageManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.font; -import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.plugin.Reloadable; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.util.CharacterUtils; @@ -9,10 +8,8 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Collection; import java.util.Optional; -import java.util.function.BiFunction; -public interface ImageManager extends Reloadable, ConfigSectionParser { - String[] CONFIG_SECTION_NAME = new String[] {"images", "image"}; +public interface FontManager extends Reloadable { Key DEFAULT_FONT = Key.of("minecraft:default"); String BYPASS_BOOK = "craftengine.filter.bypass.book"; String BYPASS_SIGN = "craftengine.filter.bypass.sign"; @@ -20,29 +17,23 @@ public interface ImageManager extends Reloadable, ConfigSectionParser { String BYPASS_COMMAND = "craftengine.filter.bypass.command"; String BYPASS_ANVIL = "craftengine.filter.bypass.anvil"; - default String[] sectionId() { - return CONFIG_SECTION_NAME; - } - - default int loadingSequence() { - return LoadingSequence.FONT; - } + ConfigSectionParser[] parsers(); boolean isDefaultFontInUse(); - boolean isIllegalCharacter(int codepoint); + boolean isIllegalCodepoint(int codepoint); - Collection fontsInUse(); + Collection fonts(); Optional bitmapImageByCodepoint(Key font, int codepoint); - default Optional getBitmapImageByChars(Key font, char[] chars) { + default Optional bitmapImageByChars(Key font, char[] chars) { return bitmapImageByCodepoint(font, CharacterUtils.charsToCodePoint(chars)); } Optional bitmapImageByImageId(Key imageId); - Optional getFontInUse(Key font); + Optional fontById(Key font); int codepointByImageId(Key imageId, int x, int y); @@ -50,15 +41,15 @@ public interface ImageManager extends Reloadable, ConfigSectionParser { return this.codepointByImageId(imageId, 0, 0); } - default char[] getCharsByImageId(Key imageId) { - return getCharsByImageId(imageId, 0, 0); + default char[] charsByImageId(Key imageId) { + return charsByImageId(imageId, 0, 0); } - default char[] getCharsByImageId(Key imageId, int x, int y) { + default char[] charsByImageId(Key imageId, int x, int y) { return Character.toChars(this.codepointByImageId(imageId, x, y)); } - String createOffsets(int offset, BiFunction tagFormatter); + String createOffsets(int offset, FontTagFormatter tagFormatter); default String createMiniMessageOffsets(int offset) { return createOffsets(offset, FormatUtils::miniMessageFont); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java index 1a2621a38..7055a82b4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontProvider.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.font; import com.google.gson.JsonObject; -public interface FontProvider { +import java.util.function.Supplier; - JsonObject getJson(); +public interface FontProvider extends Supplier { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontTagFormatter.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontTagFormatter.java new file mode 100644 index 000000000..6f2394060 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontTagFormatter.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.font; + +import java.util.function.BiFunction; + +public interface FontTagFormatter extends BiFunction { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/emoji/AbstractEmojiManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/emoji/AbstractEmojiManager.java deleted file mode 100644 index 1b670a458..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/font/emoji/AbstractEmojiManager.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.momirealms.craftengine.core.font.emoji; - -import net.momirealms.craftengine.core.plugin.CraftEngine; - -public abstract class AbstractEmojiManager implements EmojiManager { - protected CraftEngine plugin; - - public AbstractEmojiManager(CraftEngine plugin) { - this.plugin = plugin; - } - - -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/emoji/EmojiManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/emoji/EmojiManager.java deleted file mode 100644 index 04c105dce..000000000 --- a/core/src/main/java/net/momirealms/craftengine/core/font/emoji/EmojiManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.craftengine.core.font.emoji; - -import net.momirealms.craftengine.core.pack.LoadingSequence; -import net.momirealms.craftengine.core.plugin.Reloadable; -import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; - -public interface EmojiManager extends Reloadable, ConfigSectionParser { - String[] CONFIG_SECTION_NAME = new String[] {"emoji", "emojis"}; - - default String[] sectionId() { - return CONFIG_SECTION_NAME; - } - - default int loadingSequence() { - return LoadingSequence.EMOJI; - } -} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index c0fe2ed1c..7b7c042b2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -1024,7 +1024,7 @@ public abstract class AbstractPackManager implements PackManager { private void generateFonts(Path generatedPackPath) { // generate image font json - for (Font font : plugin.imageManager().fontsInUse()) { + for (Font font : plugin.imageManager().fonts()) { Key namespacedKey = font.key(); Path fontPath = generatedPackPath.resolve("assets") .resolve(namespacedKey.namespace()) @@ -1058,7 +1058,7 @@ public abstract class AbstractPackManager implements PackManager { } for (BitmapImage image : font.bitmapImages()) { - providers.add(image.getJson()); + providers.add(image.get()); } try (FileWriter fileWriter = new FileWriter(fontPath.toFile())) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java index a7036e56f..110a1db86 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/LoadingSequence.java @@ -7,7 +7,7 @@ public class LoadingSequence { public static final int BLOCK = 30; public static final int ITEM = 40; public static final int FURNITURE = 50; - public static final int FONT = 60; + public static final int IMAGE = 60; public static final int RECIPE = 70; public static final int CATEGORY = 80; public static final int SOUND = 90; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java index 958bd219a..d80d2d540 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackManager.java @@ -14,6 +14,12 @@ public interface PackManager extends Reloadable { boolean registerConfigSectionParser(ConfigSectionParser parser); + default void registerConfigSectionParsers(ConfigSectionParser[] parsers) { + for (ConfigSectionParser parser : parsers) { + registerConfigSectionParser(parser); + } + } + boolean unregisterConfigSectionParser(String id); default void unregisterConfigSectionParser(ConfigSectionParser parser) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java new file mode 100644 index 000000000..5885168b9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/ResourceLocation.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.pack; + +public class ResourceLocation { + + public static boolean isValid(final String resourceLocation) { + int index = resourceLocation.indexOf(":"); + if (index == -1) { + return isValidPath(resourceLocation); + } else { + return isValidNamespace(resourceLocation.substring(0, index)) && isValidPath(resourceLocation.substring(index + 1)); + } + } + + public static boolean validPathChar(char character) { + return character == '_' || character == '-' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '/' || character == '.'; + } + + private static boolean validNamespaceChar(char character) { + return character == '_' || character == '-' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '.'; + } + + private static boolean isValidNamespace(String namespace) { + for(int i = 0; i < namespace.length(); ++i) { + if (!validNamespaceChar(namespace.charAt(i))) { + return false; + } + } + return true; + } + + private static boolean isValidPath(String path) { + for(int i = 0; i < path.length(); ++i) { + if (!validPathChar(path.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index 32ce2432b..1f9d9826c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.plugin; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; -import net.momirealms.craftengine.core.font.ImageManager; +import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; import net.momirealms.craftengine.core.loot.VanillaLootManager; @@ -45,7 +45,7 @@ public abstract class CraftEngine implements Plugin { protected SchedulerAdapter scheduler; protected NetworkManager networkManager; protected ClassPathAppender classPathAppender; - protected ImageManager imageManager; + protected FontManager fontManager; protected PackManager packManager; protected ConfigManager configManager; protected ItemManager itemManager; @@ -106,7 +106,7 @@ public abstract class CraftEngine implements Plugin { this.translationManager.reload(); this.templateManager.reload(); this.furnitureManager.reload(); - this.imageManager.reload(); + this.fontManager.reload(); this.itemManager.reload(); this.soundManager.reload(); this.recipeManager.reload(); @@ -124,7 +124,7 @@ public abstract class CraftEngine implements Plugin { this.furnitureManager.delayedLoad(); this.itemBrowserManager.delayedLoad(); this.soundManager.delayedLoad(); - this.imageManager.delayedLoad(); + this.fontManager.delayedLoad(); // reset debugger if (ConfigManager.debug()) { this.debugger = (s) -> logger.info("[Debug] " + s.get()); @@ -153,7 +153,7 @@ public abstract class CraftEngine implements Plugin { this.worldManager.delayedInit(); this.packManager.delayedInit(); this.furnitureManager.delayedInit(); - this.imageManager.delayedInit(); + this.fontManager.delayedInit(); this.vanillaLootManager.delayedInit(); this.delayedEnable(); }); @@ -164,7 +164,7 @@ public abstract class CraftEngine implements Plugin { if (this.senderFactory != null) this.senderFactory.close(); if (this.commandManager != null) this.commandManager.unregisterFeatures(); if (this.networkManager != null) this.networkManager.shutdown(); - if (this.imageManager != null) this.imageManager.disable(); + if (this.fontManager != null) this.fontManager.disable(); if (this.packManager != null) this.packManager.disable(); if (this.itemManager != null) this.itemManager.disable(); if (this.blockManager != null) this.blockManager.disable(); @@ -241,8 +241,8 @@ public abstract class CraftEngine implements Plugin { } @Override - public ImageManager imageManager() { - return imageManager; + public FontManager imageManager() { + return fontManager; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java index fa760fadc..4f158bacf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/Plugin.java @@ -3,7 +3,7 @@ package net.momirealms.craftengine.core.plugin; import net.momirealms.craftengine.core.block.BlockManager; import net.momirealms.craftengine.core.entity.furniture.FurnitureManager; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.font.ImageManager; +import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.ItemManager; import net.momirealms.craftengine.core.item.recipe.RecipeManager; import net.momirealms.craftengine.core.loot.VanillaLootManager; @@ -55,7 +55,7 @@ public interface Plugin extends Reloadable { NetworkManager networkManager(); - ImageManager imageManager(); + FontManager imageManager(); ConfigManager configManager(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/SenderFactory.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/SenderFactory.java index f08a3caf1..a9d6f4eb8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/SenderFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/command/sender/SenderFactory.java @@ -10,9 +10,11 @@ import java.util.UUID; public abstract class SenderFactory

{ private final P plugin; + private final Sender console; public SenderFactory(P plugin) { this.plugin = plugin; + this.console = wrap(consoleCommandSender()); } protected P plugin() { @@ -35,10 +37,16 @@ public abstract class SenderFactory

{ protected abstract boolean isConsole(T sender); + protected abstract C consoleCommandSender(); + protected boolean consoleHasAllPermissions() { return true; } + public Sender console() { + return console; + } + public Sender wrap(C sender) { Objects.requireNonNull(sender, "sender"); return new AbstractSender<>(this.plugin, this, sender); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java index 52cf9559c..edb9526b0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManager.java @@ -45,4 +45,6 @@ public interface TranslationManager extends Reloadable, ConfigSectionParser { default String[] sectionId() { return CONFIG_SECTION_NAME; } + + void log(String id, String... args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java index b68f07a4e..b6c30d3f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/TranslationManagerImpl.java @@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.plugin.PluginProperties; import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor; +import net.momirealms.craftengine.core.plugin.text.minimessage.IndexedArgumentTag; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Pair; import org.jetbrains.annotations.Nullable; @@ -235,6 +236,13 @@ public class TranslationManagerImpl implements TranslationManager { return clientLangManager; } + @Override + public void log(String id, String... args) { + String translation = miniMessageTranslation(id); + if (translation == null) translation = id; + this.plugin.senderFactory().console().sendMessage(AdventureHelper.miniMessage().deserialize(translation, new IndexedArgumentTag(Arrays.stream(args).map(Component::text).toList()))); + } + private Map updateLangFile(Map previous, Path translationFile) throws IOException { DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);