diff --git a/gradle.properties b/gradle.properties index 5e310b0..8a6c380 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,9 +8,9 @@ mod_version = 1.0.0-beta.3 # Fabric minecraft_version = 1.19 -loader_version = 0.14.6 -yarn_mappings = 1.19+build.1 -fabric_version = 0.55.2+1.19 +loader_version = 0.14.8 +yarn_mappings = 1.19+build.4 +fabric_version = 0.58.0+1.19 # Dependencies checkstyle_version = 10.3 diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/api/util/StringUtils.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/api/util/StringUtils.java index 5f98b3f..ba2120d 100644 --- a/minecord-api/src/main/java/me/axieum/mcmod/minecord/api/util/StringUtils.java +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/api/util/StringUtils.java @@ -2,6 +2,7 @@ import java.text.CharacterIterator; import java.text.StringCharacterIterator; +import java.util.HashMap; import java.util.Optional; import java.util.function.Function; import java.util.regex.MatchResult; @@ -10,15 +11,24 @@ import com.vdurmont.emoji.EmojiParser; import net.dv8tion.jda.api.entities.IMentionable; +import net.minecraft.advancement.AdvancementFrame; import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; import me.axieum.mcmod.minecord.api.Minecord; +import static me.axieum.mcmod.minecord.impl.MinecordImpl.getConfig; /** * Utility methods for building and manipulating strings. */ public final class StringUtils { + // Mapping of Minecraft world identifiers to their human-readable names + public static final HashMap WORLD_NAMES = new HashMap<>(3); + // String templates for translating between Minecraft and Discord formatted strings + public static StringTemplate discordMinecraftST, minecraftDiscordST; + private StringUtils() {} /** @@ -38,9 +48,6 @@ public static String bytesToHuman(long bytes) return String.format("%.1f %cB", bytes / 1000.0, ci.current()); } - // String templates for translating between Minecraft and Discord formatted strings - public static StringTemplate discordMinecraftST, minecraftDiscordST; - static { final Pattern bold = Pattern.compile("\\*\\*(.+?)\\*\\*"); final Pattern underline = Pattern.compile("__(.+?)__"); @@ -83,21 +90,21 @@ public static String bytesToHuman(long bytes) final Pattern channel = Pattern.compile("#([^\\s]+)"); final Function resolveMention = m -> Minecord.getInstance().getJDA() - .flatMap(jda -> Optional.ofNullable(jda.getUserByTag(m.group(1), m.group(2)))) - .map(IMentionable::getAsMention) - .orElse(m.group(0)); + .flatMap(jda -> Optional.ofNullable(jda.getUserByTag(m.group(1), m.group(2)))) + .map(IMentionable::getAsMention) + .orElse(m.group(0)); final Function resolveMention2 = m -> Minecord.getInstance().getJDA() - .flatMap(jda -> jda.getGuilds().stream() - .flatMap(g -> g.getMembersByEffectiveName(m.group(1), true).stream()) - .findFirst()) - .map(IMentionable::getAsMention) - .orElse(m.group(0)); + .flatMap(jda -> jda.getGuilds().stream() + .flatMap(g -> g.getMembersByEffectiveName(m.group(1), true).stream()) + .findFirst()) + .map(IMentionable::getAsMention) + .orElse(m.group(0)); final Function resolveChannel = m -> Minecord.getInstance().getJDA() - .flatMap(jda -> jda.getTextChannelsByName(m.group(1), true).stream().findFirst()) - .map(IMentionable::getAsMention) - .orElse(m.group(0)); + .flatMap(jda -> jda.getTextChannelsByName(m.group(1), true).stream().findFirst()) + .map(IMentionable::getAsMention) + .orElse(m.group(0)); // Construct the string template minecraftDiscordST = new StringTemplate() // Collapse line breaks @@ -121,7 +128,7 @@ public static String bytesToHuman(long bytes) // Suppress @everyone and @here mentions .transform(s -> s.replace("@everyone", "@_everyone_")) .transform(s -> s.replace("@here", "@_here_")) - // Strip any left over formatting + // Strip any leftover formatting .transform(Formatting::strip); } @@ -132,7 +139,7 @@ public static String bytesToHuman(long bytes) * @param contents Discord flavoured markdown string * @return Minecraft-formatted string */ - public static String discordToMinecraft(String contents) + public static String discordToMinecraft(final String contents) { // Apply the appropriate string template against the given contents and return return discordMinecraftST.format(contents); @@ -149,4 +156,59 @@ public static String minecraftToDiscord(final String contents) // Apply the appropriate string template against the given contents and return return minecraftDiscordST.format(contents); } + + /** + * Attempts to retrieve the world name from the config files first, + * otherwise derives it from the registry key. + * + * @param world Minecraft world + * @return name of the given world + * @see #deriveWorldName(Identifier) + */ + public static String getWorldName(final World world) + { + final Identifier identifier = world.getRegistryKey().getValue(); + return getConfig().i18n.worlds.getOrDefault(identifier.toString(), deriveWorldName(identifier)); + } + + /** + * Attempts to compute and cache the world name from its registry key. + * NB: At present, the world name is not stored in any resources, apart + * from in the registry key, e.g. 'the_nether'. + * + * @param identifier Minecraft world identifier + * @return derived name of the given world identifier + */ + public static String deriveWorldName(final Identifier identifier) + { + return WORLD_NAMES.computeIfAbsent(identifier, id -> { + // Space delimited identifier path, with leading 'the' keywords removed + final String path = id.getPath().replace('_', ' ').replaceFirst("(?i)the\\s", ""); + // Capitalise the first character in each word + char[] chars = path.toCharArray(); + boolean capitalizeNext = true; + for (int i = 0; i < chars.length; i++) { + if (chars[i] == ' ') { + capitalizeNext = true; + } else if (capitalizeNext) { + chars[i] = Character.toTitleCase(chars[i]); + capitalizeNext = false; + } + } + // Return the computed world name + return new String(chars); + }); + } + + /** + * Attempts to retrieve the advancement type name from the config files + * first, otherwise uses it symbol name. + * + * @param type Minecraft advancement frame/type + * @return name of the advancement type + */ + public static String getAdvancementTypeName(final AdvancementFrame type) + { + return getConfig().i18n.advancementTypes.getOrDefault(type.getId(), type.getId()); + } } diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/MinecordImpl.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/MinecordImpl.java index c367fee..650eb10 100644 --- a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/MinecordImpl.java +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/MinecordImpl.java @@ -45,14 +45,14 @@ public void onPreLaunch() try { // Prepare the JDA client - final JDABuilder builder = JDABuilder.createDefault(getConfig().token) + final JDABuilder builder = JDABuilder.createDefault(getConfig().bot.token) // set initial bot status - .setStatus(getConfig().status.starting) + .setStatus(getConfig().bot.status.starting) // add event listeners .addEventListeners(new DiscordLifecycleListener()); // Conditionally enable member caching - if (getConfig().cacheMembers) { + if (getConfig().bot.cacheMembers) { builder.enableIntents(GatewayIntent.GUILD_MEMBERS) // enable required intents .setMemberCachePolicy(MemberCachePolicy.ALL) // cache all members .setChunkingFilter(ChunkingFilter.ALL); // eager-load all members diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/callback/ServerLifecycleCallback.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/callback/ServerLifecycleCallback.java index 73a1e31..9d9df3e 100644 --- a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/callback/ServerLifecycleCallback.java +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/callback/ServerLifecycleCallback.java @@ -20,21 +20,21 @@ public class ServerLifecycleCallback implements ServerStarting, ServerStarted, S public void onServerStarting(MinecraftServer server) { // Update the Discord bot status - Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().status.starting)); + Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().bot.status.starting)); } @Override public void onServerStarted(MinecraftServer server) { // Update the Discord bot status - Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().status.started)); + Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().bot.status.started)); } @Override public void onServerStopping(MinecraftServer server) { // Update the Discord bot status - Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().status.stopping)); + Minecord.getInstance().getJDA().ifPresent(jda -> jda.getPresence().setStatus(getConfig().bot.status.stopping)); } @Override @@ -42,7 +42,7 @@ public void onServerShutdown(MinecraftServer server, @Nullable CrashReport crash { Minecord.getInstance().getJDA().ifPresent(jda -> { // Update the Discord bot status - jda.getPresence().setStatus(getConfig().status.stopped); + jda.getPresence().setStatus(getConfig().bot.status.stopped); // Shutdown the JDA client LOGGER.info("Minecord is wrapping up..."); jda.shutdown(); diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/BotConfig.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/BotConfig.java new file mode 100644 index 0000000..ff55c8e --- /dev/null +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/BotConfig.java @@ -0,0 +1,43 @@ +package me.axieum.mcmod.minecord.impl.config; + +import me.shedaniel.autoconfig.ConfigData; +import me.shedaniel.autoconfig.annotation.Config; +import me.shedaniel.autoconfig.annotation.ConfigEntry.Category; +import me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.RequiresRestart; +import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment; +import net.dv8tion.jda.api.OnlineStatus; + +@Config(name = "bot") +public class BotConfig implements ConfigData +{ + @Comment("Token used to authenticate against your Discord bot") + @RequiresRestart + public String token = ""; + + @Category("Bot Status") + @Comment("Bot statuses relayed during the lifecycle of the server") + public StatusSchema status = new StatusSchema(); + + /** + * Bot status configuration schema. + */ + public static class StatusSchema + { + @Comment("Status while the server is starting") + public OnlineStatus starting = OnlineStatus.IDLE; + + @Comment("Status after the server has started") + public OnlineStatus started = OnlineStatus.ONLINE; + + @Comment("Status while the server is stopping") + public OnlineStatus stopping = OnlineStatus.DO_NOT_DISTURB; + + @Comment("Status after the server has stopped") + public OnlineStatus stopped = OnlineStatus.OFFLINE; + } + + @Comment("True if all guild members should be cached, in turn allowing @mentions\n" + + "NB: This requires the Privileged Gateway Intent 'Server Members' to be enabled on your Discord bot!") + @RequiresRestart + public boolean cacheMembers = false; +} diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/I18nConfig.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/I18nConfig.java new file mode 100644 index 0000000..4cde053 --- /dev/null +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/I18nConfig.java @@ -0,0 +1,33 @@ +package me.axieum.mcmod.minecord.impl.config; + +import java.util.HashMap; +import java.util.Map; + +import me.shedaniel.autoconfig.ConfigData; +import me.shedaniel.autoconfig.annotation.Config; +import me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.RequiresRestart; +import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment; + +import net.minecraft.util.Language; + +@Config(name = "i18n") +public class I18nConfig implements ConfigData +{ + @Comment("The language code used to load translations from") + @RequiresRestart + public String lang = Language.DEFAULT_LANGUAGE; + + @Comment("A mapping of Minecraft dimension IDs to their respective names") + public Map worlds = new HashMap<>(Map.ofEntries( + Map.entry("minecraft:overworld", "Overworld"), + Map.entry("minecraft:the_nether", "Nether"), + Map.entry("minecraft:the_end", "The End") + )); + + @Comment("A mapping of advancement types to their respective names") + public Map advancementTypes = new HashMap<>(Map.ofEntries( + Map.entry("task", "task"), + Map.entry("challenge", "challenge"), + Map.entry("goal", "goal") + )); +} diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/MinecordConfig.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/MinecordConfig.java index 4aa4128..fe633d5 100644 --- a/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/MinecordConfig.java +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/impl/config/MinecordConfig.java @@ -1,62 +1,36 @@ package me.axieum.mcmod.minecord.impl.config; import me.shedaniel.autoconfig.AutoConfig; -import me.shedaniel.autoconfig.ConfigData; import me.shedaniel.autoconfig.ConfigHolder; import me.shedaniel.autoconfig.annotation.Config; import me.shedaniel.autoconfig.annotation.ConfigEntry.Category; -import me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.RequiresRestart; import me.shedaniel.autoconfig.serializer.ConfigSerializer; import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; -import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment; -import net.dv8tion.jda.api.OnlineStatus; +import me.shedaniel.autoconfig.serializer.PartitioningSerializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -@Config(name = "minecord/bot") -public class MinecordConfig implements ConfigData +@Config(name = "minecord") +public class MinecordConfig extends PartitioningSerializer.GlobalData { - @Comment("Token used to authenticate against your Discord bot") - @RequiresRestart - public String token = ""; + @Category("bot") + public BotConfig bot = new BotConfig(); - @Category("Bot Status") - @Comment("Bot statuses relayed during the lifecycle of the server") - public StatusSchema status = new StatusSchema(); - - /** - * Bot status configuration schema. - */ - public static class StatusSchema - { - @Comment("Status while the server is starting") - public OnlineStatus starting = OnlineStatus.IDLE; - - @Comment("Status after the server has started") - public OnlineStatus started = OnlineStatus.ONLINE; - - @Comment("Status while the server is stopping") - public OnlineStatus stopping = OnlineStatus.DO_NOT_DISTURB; - - @Comment("Status after the server has stopped") - public OnlineStatus stopped = OnlineStatus.OFFLINE; - } - - @Comment("True if all guild members should be cached, in turn allowing @mentions\n" - + "NB: This requires the Privileged Gateway Intent 'Server Members' to be enabled on your Discord bot!") - @RequiresRestart - public boolean cacheMembers = false; + @Category("i18n") + public I18nConfig i18n = new I18nConfig(); /** * Registers and prepares a new configuration instance. * * @return registered config holder - * @see me.shedaniel.autoconfig.AutoConfig#register(Class, ConfigSerializer.Factory) + * @see AutoConfig#register(Class, ConfigSerializer.Factory) */ public static ConfigHolder init() { // Register the config - ConfigHolder holder = AutoConfig.register(MinecordConfig.class, JanksonConfigSerializer::new); + ConfigHolder holder = AutoConfig.register( + MinecordConfig.class, PartitioningSerializer.wrap(JanksonConfigSerializer::new) + ); // Listen for when the server is reloading (i.e. /reload), and reload the config ServerLifecycleEvents.START_DATA_PACK_RELOAD.register((s, m) -> diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/LanguageMixin.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/LanguageMixin.java new file mode 100644 index 0000000..72d79d3 --- /dev/null +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/LanguageMixin.java @@ -0,0 +1,76 @@ +package me.axieum.mcmod.minecord.mixin.api; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.function.BiConsumer; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonParseException; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.util.Language; + +import net.fabricmc.loader.api.FabricLoader; + +import static me.axieum.mcmod.minecord.impl.MinecordImpl.LOGGER; +import static me.axieum.mcmod.minecord.impl.MinecordImpl.getConfig; + +/** + * Injects into, and loads all modded translation files. + */ +@Mixin(Language.class) +public abstract class LanguageMixin +{ + // The default language path already loaded by Minecraft - we'll skip loading this + private static final String DEFAULT_LANGUAGE_PATH = "/assets/minecraft/lang/" + Language.DEFAULT_LANGUAGE + ".json"; + + /** + * Loads all modded translations for use when resolving translatable text. + * + * @param cir mixin callback info + * @param builder language immutable mapping builder + * @param biConsumer language file key/value consumer + */ + @SuppressWarnings("InvalidInjectorMethodSignature") + @Inject( + method = "create", + at = @At( + value = "INVOKE", + target = "Lcom/google/common/collect/ImmutableMap$Builder;build()Lcom/google/common/collect/ImmutableMap;" + ), + locals = LocalCapture.CAPTURE_FAILSOFT + ) + private static void create( + CallbackInfoReturnable cir, + ImmutableMap.Builder builder, + BiConsumer biConsumer + ) + { + final String langCode = getConfig().i18n.lang; + LOGGER.info("Begin loading '{}' modded translation files...", langCode); + FabricLoader.getInstance().getAllMods().forEach(mod -> { + final String modId = mod.getMetadata().getId(); + mod + // Attempt to find the configured translation file + .findPath("assets/" + modId + "/lang/" + langCode + ".json") + // Otherwise, fallback to the default translation file + .or(() -> mod.findPath("assets/" + modId + "/lang/" + Language.DEFAULT_LANGUAGE + ".json")) + // Skip Minecraft translations since they are *already* loaded + .filter(path -> !DEFAULT_LANGUAGE_PATH.equals(path.toString())) + // If we found a translation file, load it + .ifPresent(path -> { + LOGGER.info("Loading modded translation file: {}", path.toString()); + try (InputStream is = Files.newInputStream(path)) { + Language.load(is, biConsumer); + } catch (IOException | JsonParseException e) { + LOGGER.error("Failed to load modded translation file '{}'", path.toString(), e); + } + }); + }); + } +} diff --git a/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/MinecraftServerMixin.java b/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/MinecraftServerMixin.java index 0d705dc..cc28a13 100644 --- a/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/MinecraftServerMixin.java +++ b/minecord-api/src/main/java/me/axieum/mcmod/minecord/mixin/api/MinecraftServerMixin.java @@ -24,10 +24,10 @@ public abstract class MinecraftServerMixin /** * Broadcasts a server shutdown event, be it gracefully or forcefully exited. * - * @param info mixin callback info + * @param ci mixin callback info */ @Inject(method = "runServer", at = @At("TAIL")) - private void runServer(CallbackInfo info) + private void runServer(CallbackInfo ci) { ServerShutdownCallback.EVENT.invoker().onServerShutdown((MinecraftServer) (Object) this, crashReport); } @@ -36,10 +36,10 @@ private void runServer(CallbackInfo info) * Captures any server crash reports. * * @param crashReport Minecraft crash report being set - * @param info mixin callback info + * @param ci mixin callback info */ @Inject(method = "setCrashReport", at = @At("TAIL")) - private void setCrashReport(CrashReport crashReport, CallbackInfo info) + private void setCrashReport(CrashReport crashReport, CallbackInfo ci) { MinecraftServerMixin.crashReport = crashReport; } diff --git a/minecord-api/src/main/resources/minecord.mixins.json b/minecord-api/src/main/resources/minecord.mixins.json index 8b06624..f6547ee 100644 --- a/minecord-api/src/main/resources/minecord.mixins.json +++ b/minecord-api/src/main/resources/minecord.mixins.json @@ -6,6 +6,7 @@ "mixins": [], "server": [ "CrashReportAccessor", + "LanguageMixin", "MinecraftServerMixin" ], "client": [], diff --git a/minecord-api/src/test/java/me/axieum/mcmod/minecord/api/util/StringUtilsTests.java b/minecord-api/src/test/java/me/axieum/mcmod/minecord/api/util/StringUtilsTests.java index 97fab70..6e93afc 100644 --- a/minecord-api/src/test/java/me/axieum/mcmod/minecord/api/util/StringUtilsTests.java +++ b/minecord-api/src/test/java/me/axieum/mcmod/minecord/api/util/StringUtilsTests.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import net.minecraft.util.Identifier; + @DisplayName("String Utils") public class StringUtilsTests { @@ -211,4 +213,18 @@ public void stripFormatting() ); } } + + @Test + @DisplayName("Derive World Name") + public void deriveWorldName() + { + assertEquals( + "Overworld", + StringUtils.deriveWorldName(new Identifier("minecraft", "overworld")) + ); + assertEquals( + "Deep Dark", + StringUtils.deriveWorldName(new Identifier("extrautils", "the_deep_dark")) + ); + } } diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtils.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtils.java deleted file mode 100644 index 45265f9..0000000 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.axieum.mcmod.minecord.api.chat.util; - -import java.util.HashMap; - -import net.minecraft.util.Identifier; -import net.minecraft.world.World; - -import static me.axieum.mcmod.minecord.impl.chat.MinecordChat.getConfig; - -/** - * Utility methods for chat related strings. - */ -public final class ChatStringUtils -{ - // Mapping of Minecraft world identifiers to their human-readable names - public static final HashMap WORLD_NAMES = new HashMap<>(3); - - private ChatStringUtils() {} - - /** - * Attempts to retrieve the world name from the config files first, - * otherwise derives it from the registry key. - * - * @param world Minecraft world - * @return name of the given world - * @see #deriveWorldName(Identifier) - */ - public static String getWorldName(final World world) - { - final Identifier identifier = world.getRegistryKey().getValue(); - return getConfig().worldNames.getOrDefault(identifier.toString(), deriveWorldName(identifier)); - } - - /** - * Attempts to compute and cache the world name from its registry key. - * NB: At present, the world name is not stored in any resources, apart - * from in the registry key, e.g. 'the_nether'. - * - * @param identifier Minecraft world identifier - * @return derived name of the given world identifier - */ - public static String deriveWorldName(final Identifier identifier) - { - return WORLD_NAMES.computeIfAbsent(identifier, id -> { - // Space delimited identifier path, with leading 'the' keywords removed - final String path = id.getPath().replace('_', ' ').replaceFirst("(?i)the\\s", ""); - // Capitalise the first character in each word - char[] chars = path.toCharArray(); - boolean capitalizeNext = true; - for (int i = 0; i < chars.length; i++) { - if (chars[i] == ' ') { - capitalizeNext = true; - } else if (capitalizeNext) { - chars[i] = Character.toTitleCase(chars[i]); - capitalizeNext = false; - } - } - // Return the computed world name - return new String(chars); - }); - } -} diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/EntityDeathCallback.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/EntityDeathCallback.java index 726b7b6..d35d534 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/EntityDeathCallback.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/EntityDeathCallback.java @@ -8,8 +8,8 @@ import me.axieum.mcmod.minecord.api.Minecord; import me.axieum.mcmod.minecord.api.chat.event.ChatPlaceholderEvents; import me.axieum.mcmod.minecord.api.chat.event.minecraft.EntityDeathEvents; -import me.axieum.mcmod.minecord.api.chat.util.ChatStringUtils; import me.axieum.mcmod.minecord.api.util.StringTemplate; +import me.axieum.mcmod.minecord.api.util.StringUtils; import me.axieum.mcmod.minecord.impl.chat.util.DiscordDispatcher; public class EntityDeathCallback implements EntityDeathEvents.Entity @@ -36,7 +36,7 @@ public void onEntityDeath(LivingEntity entity, DamageSource source) "cause", source.getDeathMessage(entity).getString().replaceFirst(entityName, "").trim() ); // The name of the world the entity died in - st.add("world", ChatStringUtils.getWorldName(entity.world)); + st.add("world", StringUtils.getWorldName(entity.world)); // The X coordinate of where the entity died st.add("x", String.valueOf((int) entity.prevX)); // The Y coordinate of where the entity died diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerAdvancementCallback.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerAdvancementCallback.java index abe47a7..458622f 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerAdvancementCallback.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerAdvancementCallback.java @@ -8,6 +8,7 @@ import me.axieum.mcmod.minecord.api.chat.event.ChatPlaceholderEvents; import me.axieum.mcmod.minecord.api.chat.event.minecraft.GrantCriterionCallback; import me.axieum.mcmod.minecord.api.util.StringTemplate; +import me.axieum.mcmod.minecord.api.util.StringUtils; import me.axieum.mcmod.minecord.impl.chat.util.DiscordDispatcher; public class PlayerAdvancementCallback implements GrantCriterionCallback @@ -31,7 +32,7 @@ public void onGrantCriterion(ServerPlayerEntity player, Advancement advancement, // The player's display name st.add("player", player.getDisplayName().getString()); // The type of advancement - st.add("type", info.getFrame().getId()); + st.add("type", StringUtils.getAdvancementTypeName(info.getFrame())); // The title of the advancement st.add("title", info.getTitle().getString()); // A description of the advancement diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChangeWorldCallback.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChangeWorldCallback.java index 673297d..52c23fc 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChangeWorldCallback.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChangeWorldCallback.java @@ -7,8 +7,8 @@ import me.axieum.mcmod.minecord.api.Minecord; import me.axieum.mcmod.minecord.api.chat.event.ChatPlaceholderEvents; -import me.axieum.mcmod.minecord.api.chat.util.ChatStringUtils; import me.axieum.mcmod.minecord.api.util.StringTemplate; +import me.axieum.mcmod.minecord.api.util.StringUtils; import me.axieum.mcmod.minecord.impl.chat.util.DiscordDispatcher; public class PlayerChangeWorldCallback implements ServerEntityWorldChangeEvents.AfterPlayerChange @@ -28,7 +28,7 @@ public void afterChangeWorld(ServerPlayerEntity player, ServerWorld origin, Serv // The player's display name st.add("player", player.getDisplayName().getString()); // The name of the world the player entered - st.add("world", ChatStringUtils.getWorldName(dest)); + st.add("world", StringUtils.getWorldName(dest)); // The X coordinate of where the player entered st.add("x", String.valueOf(player.getBlockX())); // The Y coordinate of where the player entered @@ -36,7 +36,7 @@ public void afterChangeWorld(ServerPlayerEntity player, ServerWorld origin, Serv // The Z coordinate of where the player entered st.add("z", String.valueOf(player.getBlockZ())); // The name of the world the player left - st.add("origin", ChatStringUtils.getWorldName(origin)); + st.add("origin", StringUtils.getWorldName(origin)); // The X coordinate of where the player left st.add("origin_x", String.valueOf((int) player.prevX)); // The Y coordinate of where the player left diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChatCallback.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChatCallback.java index 1989fab..8394823 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChatCallback.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/impl/chat/callback/minecraft/PlayerChatCallback.java @@ -7,7 +7,6 @@ import me.axieum.mcmod.minecord.api.Minecord; import me.axieum.mcmod.minecord.api.chat.event.ChatPlaceholderEvents; import me.axieum.mcmod.minecord.api.chat.event.minecraft.ReceiveChatCallback; -import me.axieum.mcmod.minecord.api.chat.util.ChatStringUtils; import me.axieum.mcmod.minecord.api.util.StringTemplate; import me.axieum.mcmod.minecord.api.util.StringUtils; import me.axieum.mcmod.minecord.impl.chat.util.DiscordDispatcher; @@ -29,7 +28,7 @@ public void onReceiveChat(ServerPlayerEntity player, FilteredMessage worldNames = new HashMap<>(Map.ofEntries( - Map.entry("minecraft:overworld", "Overworld"), - Map.entry("minecraft:the_nether", "Nether"), - Map.entry("minecraft:the_end", "The End") - )); - /** * Determines whether the given channel identifier relates to any chat entries. * diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/LivingEntityMixin.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/LivingEntityMixin.java index c7434e0..9d787d2 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/LivingEntityMixin.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/LivingEntityMixin.java @@ -20,11 +20,11 @@ public abstract class LivingEntityMixin * Broadcasts any animal or monster deaths. * * @param source damage source - * @param info mixin callback info + * @param ci mixin callback info */ @Inject(method = "onDeath", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;sendEntityStatus(" + "Lnet/minecraft/entity/Entity;B)V")) - public void onDeath(DamageSource source, CallbackInfo info) + public void onDeath(DamageSource source, CallbackInfo ci) { EntityDeathEvents.ANIMAL_MONSTER.invoker().onEntityDeath((LivingEntity) (Object) this, source); } diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/PlayerAdvancementTrackerMixin.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/PlayerAdvancementTrackerMixin.java index 15104bb..a1dd7c5 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/PlayerAdvancementTrackerMixin.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/PlayerAdvancementTrackerMixin.java @@ -26,11 +26,11 @@ public abstract class PlayerAdvancementTrackerMixin * * @param advancement parent advancement * @param criterion name of the criterion granted - * @param info mixin callback info + * @param cir mixin callback info */ @Inject(method = "grantCriterion", at = @At(value = "INVOKE", target = "Lnet/minecraft/advancement/" + "AdvancementRewards;apply(Lnet/minecraft/server/network/ServerPlayerEntity;)V")) - public void grantCriterion(Advancement advancement, String criterion, CallbackInfoReturnable info) + public void grantCriterion(Advancement advancement, String criterion, CallbackInfoReturnable cir) { GrantCriterionCallback.EVENT.invoker().onGrantCriterion(owner, advancement, criterion); } diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayNetworkHandlerMixin.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayNetworkHandlerMixin.java index 2c0e704..1becba5 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayNetworkHandlerMixin.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayNetworkHandlerMixin.java @@ -26,7 +26,7 @@ public abstract class ServerPlayNetworkHandlerMixin * Broadcasts any player chat messages. * * @param message received message contents - * @param info mixin callback info + * @param ci mixin callback info */ @Inject( method = "handleDecoratedMessage", @@ -35,7 +35,7 @@ public abstract class ServerPlayNetworkHandlerMixin + "Lnet/minecraft/server/network/ServerPlayerEntity;" + "Lnet/minecraft/util/registry/RegistryKey;)V") ) - private void onChatMessage(FilteredMessage message, CallbackInfo info) + private void onChatMessage(FilteredMessage message, CallbackInfo ci) { ReceiveChatCallback.EVENT.invoker().onReceiveChat(player, message); } diff --git a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayerEntityMixin.java b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayerEntityMixin.java index 1d43a99..4dddfd1 100644 --- a/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayerEntityMixin.java +++ b/minecord-chat/src/main/java/me/axieum/mcmod/minecord/mixin/chat/ServerPlayerEntityMixin.java @@ -20,11 +20,11 @@ public abstract class ServerPlayerEntityMixin * Broadcasts any player deaths. * * @param source damage source - * @param info mixin callback info + * @param ci mixin callback info */ @Inject(method = "onDeath", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;sendEntityStatus(" + "Lnet/minecraft/entity/Entity;B)V")) - public void onDeath(DamageSource source, CallbackInfo info) + public void onDeath(DamageSource source, CallbackInfo ci) { EntityDeathEvents.PLAYER.invoker().onPlayerDeath((ServerPlayerEntity) (Object) this, source); } diff --git a/minecord-chat/src/test/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtilsTests.java b/minecord-chat/src/test/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtilsTests.java deleted file mode 100644 index 2360f73..0000000 --- a/minecord-chat/src/test/java/me/axieum/mcmod/minecord/api/chat/util/ChatStringUtilsTests.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.axieum.mcmod.minecord.api.chat.util; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import net.minecraft.util.Identifier; - -@DisplayName("Chat String Utils") -public class ChatStringUtilsTests -{ - @Test - @DisplayName("Derive World Name") - public void deriveWorldName() - { - assertEquals( - "Overworld", - ChatStringUtils.deriveWorldName(new Identifier("minecraft", "overworld")) - ); - assertEquals( - "Deep Dark", - ChatStringUtils.deriveWorldName(new Identifier("extrautils", "the_deep_dark")) - ); - } -}