From db56afd319f48bad3ed08d5769a355c93bf0047c Mon Sep 17 00:00:00 2001 From: ChomeNS <95471003+ChomeNS@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:57:58 +0700 Subject: [PATCH] feat: initial 1.21.5 with adventure still not being updated --- build.gradle | 2 +- .../chayapak1/chomens_bot/Configuration.java | 3 +- .../chomens_bot/data/bossbar/BotBossBar.java | 4 +- .../plugins/BossbarManagerPlugin.java | 4 +- .../chomens_bot/plugins/ChatPlugin.java | 13 +- .../chomens_bot/plugins/CorePlugin.java | 6 +- .../chomens_bot/plugins/PositionPlugin.java | 1 - .../plugins/ScreensharePlugin.java | 4 +- .../chomens_bot/plugins/SelfCarePlugin.java | 4 +- .../chomens_bot/util/SNBTUtilities.java | 140 ++++++++++++++++++ src/main/resources/default-config.yml | 2 + 11 files changed, 159 insertions(+), 24 deletions(-) create mode 100644 src/main/java/me/chayapak1/chomens_bot/util/SNBTUtilities.java diff --git a/build.gradle b/build.gradle index 77faf175..07263f7a 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ repositories { } dependencies { - implementation 'org.geysermc.mcprotocollib:protocol:1.21.4-SNAPSHOT' + implementation 'org.geysermc.mcprotocollib:protocol:1.21.5-SNAPSHOT' implementation 'net.kyori:adventure-text-serializer-legacy:4.19.0' implementation 'com.google.code.gson:gson:2.12.1' implementation 'com.google.guava:guava:33.4.0-jre' diff --git a/src/main/java/me/chayapak1/chomens_bot/Configuration.java b/src/main/java/me/chayapak1/chomens_bot/Configuration.java index 5a0f8f87..9f84f178 100644 --- a/src/main/java/me/chayapak1/chomens_bot/Configuration.java +++ b/src/main/java/me/chayapak1/chomens_bot/Configuration.java @@ -76,7 +76,7 @@ public class Configuration { } public static class Core { - public String customName = "{\"text\":\"@\"}"; + public String customName = "{text:'@'}"; } public static class Position { @@ -172,6 +172,7 @@ public class Configuration { public boolean useCore = true; public boolean useCorePlaceBlock = false; public boolean useChat = false; + public boolean useSNBTComponents = true; public boolean coreCommandSpy = true; public boolean resolveSRV = true; public int reconnectDelay = 2000; diff --git a/src/main/java/me/chayapak1/chomens_bot/data/bossbar/BotBossBar.java b/src/main/java/me/chayapak1/chomens_bot/data/bossbar/BotBossBar.java index befd3f64..4b28e05b 100644 --- a/src/main/java/me/chayapak1/chomens_bot/data/bossbar/BotBossBar.java +++ b/src/main/java/me/chayapak1/chomens_bot/data/bossbar/BotBossBar.java @@ -1,8 +1,8 @@ package me.chayapak1.chomens_bot.data.bossbar; import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.util.SNBTUtilities; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.geysermc.mcprotocollib.protocol.data.game.BossBarColor; import org.geysermc.mcprotocollib.protocol.data.game.BossBarDivision; @@ -65,7 +65,7 @@ public class BotBossBar extends BossBar { this.title = title; - final String serialized = GsonComponentSerializer.gson().serialize(title); + final String serialized = SNBTUtilities.fromComponent(bot, title); bot.core.run("minecraft:bossbar set " + id + " name " + serialized); diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java index f2c5cb4a..c82ac12c 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/BossbarManagerPlugin.java @@ -6,7 +6,7 @@ import me.chayapak1.chomens_bot.data.bossbar.BotBossBar; import me.chayapak1.chomens_bot.data.listener.Listener; import me.chayapak1.chomens_bot.data.player.PlayerEntry; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import me.chayapak1.chomens_bot.util.SNBTUtilities; import org.geysermc.mcprotocollib.network.Session; import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; import org.geysermc.mcprotocollib.network.packet.Packet; @@ -196,7 +196,7 @@ public class BossbarManagerPlugin implements Listener { final Component title = bossBar.secret; - final String stringTitle = GsonComponentSerializer.gson().serialize(title); + final String stringTitle = SNBTUtilities.fromComponent(bot, title); bot.core.run("minecraft:bossbar add " + name + " " + stringTitle); diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/ChatPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/ChatPlugin.java index 163c8d32..49271eaa 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/ChatPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/ChatPlugin.java @@ -9,16 +9,12 @@ import me.chayapak1.chomens_bot.data.chat.ChatParser; import me.chayapak1.chomens_bot.data.chat.PlayerMessage; import me.chayapak1.chomens_bot.data.listener.Listener; import me.chayapak1.chomens_bot.data.player.PlayerEntry; -import me.chayapak1.chomens_bot.util.ComponentUtilities; -import me.chayapak1.chomens_bot.util.IllegalCharactersUtilities; -import me.chayapak1.chomens_bot.util.StringUtilities; -import me.chayapak1.chomens_bot.util.UUIDUtilities; +import me.chayapak1.chomens_bot.util.*; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; import org.geysermc.mcprotocollib.network.Session; @@ -300,7 +296,8 @@ public class ChatPlugin implements Listener { 0L, null, 0, - new BitSet() + new BitSet(), + 0 )); } @@ -359,7 +356,7 @@ public class ChatPlugin implements Listener { final String stringified = ComponentUtilities.stringifySectionSign(component).replace("ยง", "&"); send(stringified); } else { - bot.core.run("minecraft:tellraw " + targets + " " + GsonComponentSerializer.gson().serialize(component)); + bot.core.run("minecraft:tellraw " + targets + " " + SNBTUtilities.fromComponent(bot, component)); } } @@ -369,7 +366,7 @@ public class ChatPlugin implements Listener { public void actionBar (final Component component, final String targets) { if (bot.options.useChat) return; - bot.core.run("minecraft:title " + targets + " actionbar " + GsonComponentSerializer.gson().serialize(component)); + bot.core.run("minecraft:title " + targets + " actionbar " + SNBTUtilities.fromComponent(bot, component)); } public void actionBar (final Component component, final UUID uuid) { actionBar(component, UUIDUtilities.selector(uuid)); } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java index 73374498..8cc0afad 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java @@ -541,11 +541,7 @@ public class CorePlugin implements Listener { useChat ? "" - : "{CustomName:'" + - bot.config.core.customName - .replace("\\", "\\\\") - .replace("'", "\\'") + - "'}" + : "{CustomName:" + bot.config.core.customName + "}" ); if (useChat) bot.chat.sendCommandInstantly(command); diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java index 622f141a..00a90479 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/PositionPlugin.java @@ -11,7 +11,6 @@ import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.*; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.spawn.ClientboundAddEntityPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/ScreensharePlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/ScreensharePlugin.java index c3aa5d20..cff19a7e 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/ScreensharePlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/ScreensharePlugin.java @@ -1,9 +1,9 @@ package me.chayapak1.chomens_bot.plugins; import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.util.SNBTUtilities; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.cloudburstmc.math.vector.Vector3i; import java.awt.*; @@ -141,7 +141,7 @@ public class ScreensharePlugin { } for (int i = 0; i < names.size(); i++) { - bot.core.run("minecraft:data merge entity @e[tag=" + tags.get(i) + ",limit=1] {text:'" + GsonComponentSerializer.gson().serialize(names.get(i)) + "'}"); + bot.core.run("minecraft:data merge entity @e[tag=" + tags.get(i) + ",limit=1] {text:" + SNBTUtilities.fromComponent(bot, names.get(i)) + "}"); } } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/SelfCarePlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/SelfCarePlugin.java index fd2ba6e6..b91701d9 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/SelfCarePlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/SelfCarePlugin.java @@ -159,12 +159,12 @@ public class SelfCarePlugin implements Listener { final GameEvent notification = packet.getNotification(); final GameEventValue value = packet.getValue(); - if (notification == GameEvent.ENTER_CREDITS && bot.config.selfCare.endCredits) { + if (notification == GameEvent.WIN_GAME && bot.config.selfCare.endCredits) { bot.session.send(new ServerboundClientCommandPacket(ClientCommand.RESPAWN)); return; } - if (notification == GameEvent.CHANGE_GAMEMODE) gamemode = (GameMode) value; + if (notification == GameEvent.CHANGE_GAME_MODE) gamemode = (GameMode) value; } private void packetReceived (final ClientboundEntityEventPacket packet) { diff --git a/src/main/java/me/chayapak1/chomens_bot/util/SNBTUtilities.java b/src/main/java/me/chayapak1/chomens_bot/util/SNBTUtilities.java new file mode 100644 index 00000000..32b4f033 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/util/SNBTUtilities.java @@ -0,0 +1,140 @@ +package me.chayapak1.chomens_bot.util; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import me.chayapak1.chomens_bot.Bot; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; + +import java.util.Map; + +// for 1.21.5 SNBT components, not sure how performant this is +// since i'm not good at writing performant code, but this should +// reduce the size of the result being sent to the server by quite a bit +public class SNBTUtilities { + private static final String QUOTE = "'"; + + private static final String COMMA = ","; + + private static final String BEGIN_OBJECT = "{"; + private static final String COLON = ":"; + private static final String END_OBJECT = "}"; + + private static final String BEGIN_ARRAY = "["; + private static final String END_ARRAY = "]"; + + public static String fromComponent (final Bot bot, final Component component) { + if (!bot.options.useSNBTComponents) return GsonComponentSerializer.gson().serialize(component); + + final JsonElement json = GsonComponentSerializer.gson().serializeToTree(component); + + return fromJson(json); + } + + // RIPPED from https://minecraft.wiki/w/NBT_format#Conversion_from_JSON + public static String fromJson (final JsonElement json) { + if (json.isJsonPrimitive()) { + final JsonPrimitive primitive = json.getAsJsonPrimitive(); + + if (primitive.isString()) return + // don't wrap the string with quotes when not needed + // like {abc:def} instead of {abc:'def'} + needQuotes(primitive.getAsString()) + ? QUOTE + escapeString(primitive.getAsString()) + QUOTE + : primitive.getAsString(); + else if (primitive.isBoolean()) return primitive.getAsBoolean() ? "1b" : "0b"; + else if (primitive.isNumber()) return String.valueOf(primitive.getAsNumber()); + } else if (json.isJsonArray()) { + final StringBuilder stringBuilder = new StringBuilder(BEGIN_ARRAY); + + boolean notEmpty = false; + + for (final JsonElement element : json.getAsJsonArray()) { + notEmpty = true; + stringBuilder.append(fromJson(element)); + stringBuilder.append(COMMA); + } + + if (notEmpty) stringBuilder.deleteCharAt(stringBuilder.length() - 1); // removes comma + + stringBuilder.append(END_ARRAY); + + return stringBuilder.toString(); + } else if (json.isJsonObject()) { + final StringBuilder stringBuilder = new StringBuilder(BEGIN_OBJECT); + + boolean notEmpty = false; + + for (final Map.Entry entry : json.getAsJsonObject().entrySet()) { + notEmpty = true; + + // FIXME: only works in 1.21.5 adventure, at the time of writing this, + // the 1.21.5 PR is still not merged yet, so i am doing + // a lazy fix for the hover event `contents` being replaced with + // `value` and the hoverEvent and clickEvent being snake case, + // so some things work + + if (entry.getKey().equals("contents")) { + stringBuilder.append("value"); + + // no click_event replacement yet + + } else { + stringBuilder.append(convertCamelCaseToSnake(entry.getKey())); + } + stringBuilder.append(COLON); + stringBuilder.append(fromJson(entry.getValue())); + stringBuilder.append(COMMA); + } + + if (notEmpty) stringBuilder.deleteCharAt(stringBuilder.length() - 1); // removes comma + + stringBuilder.append(END_OBJECT); + + return stringBuilder.toString(); + } + + return QUOTE + QUOTE; + } + + private static String escapeString (final String string) { + return string + .replace("\\", "\\\\") + .replace("'", "\\'") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t") + .replace("\b", "\\b") + .replace("\f", "\\f"); + } + + // should this be in StringUtilities? + public static boolean needQuotes (final String string) { + if (string.isBlank()) return true; + for (int i = 0; i < string.length(); i++) { + final char c = string.charAt(i); + // even though we can do like `abc123.among.us__69` without quotes + // i am too lazy to implement that + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + return true; + } + } + return false; + } + + // https://www.baeldung.com/java-camel-snake-case-conversion + // 3. Manual Approach + // FIXME: this is optional after adventure 1.21.5, remove this afterward + // it's why this is here instead of in StringUtilities + public static String convertCamelCaseToSnake (final String input) { + final StringBuilder result = new StringBuilder(); + for (final char c : input.toCharArray()) { + if (Character.isUpperCase(c)) { + result.append("_").append(Character.toLowerCase(c)); + } else { + result.append(c); + } + } + return result.toString(); + } +} diff --git a/src/main/resources/default-config.yml b/src/main/resources/default-config.yml index caa193a2..d96d07ad 100644 --- a/src/main/resources/default-config.yml +++ b/src/main/resources/default-config.yml @@ -141,6 +141,7 @@ bots: # useCore - if enabled it just sends the command using chat instead of using core. recommended to enable useChat too when this is disabled # useCorePlaceBlock - uses the place block core instead of the main core. only used if useCore is enabled # useChat - when the bot tellraws it will chat instead of using the core to run tellraw + # useSNBTComponents - if the server is >=1.21.5, it's pretty likely that you can enable this since 1.21.5 supports SNBT components # coreCommandSpy - set to true if server supports enabling player's commandspy through command block, defaults to true # resolveSRV - whether to resolve SRV records on the server. the notchian minecraft doesn't resolve them # essentialsMessages - the messages in essentials that the bot uses for self care (no example is intentional) @@ -160,6 +161,7 @@ bots: # useCore: true # useCorePlaceBlock: false # useChat: false + # useSNBTComponents: true # coreCommandSpy: true # resolveSRV: true # reconnectDelay: 2000