From c8668f6206b0e5f62f8a798ed0cbf081d455c76e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:12:36 +0000 Subject: [PATCH 1/6] Initial plan From 7a55fed5f2e9baf9f899b92ddcbd7930f771feea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:14:38 +0000 Subject: [PATCH 2/6] fix: support Purpur 26.1.2 in version mapping --- .../magemonkey/codex/compat/VersionManager.java | 2 +- .../studio/magemonkey/codex/core/Version.java | 2 +- .../codex/compat/VersionManagerTest.java | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java diff --git a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java index c03cbb9e..e0650c24 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java @@ -67,7 +67,7 @@ private static String getPackageFromVersion(String version) { case "1.21.6", "1.21.7", "1.21.8" -> "v1_21_7"; case "1.21.9", "1.21.10" -> "v1_21_10"; case "1.21.11" -> "v1_21_11"; - case "26.2" -> "v26_2"; + case "26.1.2", "26.2" -> "v26_2"; default -> throw new UnsupportedVersionException("Unknown version " + version); }; } diff --git a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java index 7e8cfa30..26534051 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java @@ -56,7 +56,7 @@ else if (versionRaw.equals("craftbukkit")) { case "1.21.6-R0.1-SNAPSHOT", "1.21.7-R0.1-SNAPSHOT", "1.21.8-R0.1-SNAPSHOT" -> Version.V1_21_R5; case "1.21.9-R0.1-SNAPSHOT", "1.21.10-R0.1-SNAPSHOT" -> Version.V1_21_R6; case "1.21.11-R0.1-SNAPSHOT" -> Version.V1_21_R7; - case "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R1; + case "26.1.2-R0.1-SNAPSHOT", "26.1.2", "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R1; default -> throw new IllegalStateException("Unexpected version: " + version); }; } else diff --git a/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java b/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java new file mode 100644 index 00000000..abb4ca81 --- /dev/null +++ b/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java @@ -0,0 +1,17 @@ +package studio.magemonkey.codex.compat; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +class VersionManagerTest { + + @Test + void getPackageFromVersion_supportsPurpur2612() throws Exception { + Method method = VersionManager.class.getDeclaredMethod("getPackageFromVersion", String.class); + method.setAccessible(true); + + Object packageName = method.invoke(null, "26.1.2"); + assert packageName.equals("v26_2"); + } +} From fbf1ba69f7525bfedbb6ff29acf9097e9ac825c4 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:01:19 +0200 Subject: [PATCH 3/6] split 26_1 and 26_2 --- .../magemonkey/codex/compat/VersionManager.java | 3 ++- .../studio/magemonkey/codex/core/Version.java | 6 ++++-- .../codex/compat/VersionManagerTest.java | 17 ----------------- 3 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java diff --git a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java index e0650c24..144db875 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java @@ -67,7 +67,8 @@ private static String getPackageFromVersion(String version) { case "1.21.6", "1.21.7", "1.21.8" -> "v1_21_7"; case "1.21.9", "1.21.10" -> "v1_21_10"; case "1.21.11" -> "v1_21_11"; - case "26.1.2", "26.2" -> "v26_2"; + case "26.1.2" -> "v26_1"; + case "26.2" -> "v26_2"; default -> throw new UnsupportedVersionException("Unknown version " + version); }; } diff --git a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java index 26534051..b450de19 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java @@ -26,7 +26,8 @@ public enum Version { V1_21_R5, V1_21_R6, V1_21_R7, - V26_R1; + V26_R1, + V26_R2; public static final Version CURRENT; @@ -56,7 +57,8 @@ else if (versionRaw.equals("craftbukkit")) { case "1.21.6-R0.1-SNAPSHOT", "1.21.7-R0.1-SNAPSHOT", "1.21.8-R0.1-SNAPSHOT" -> Version.V1_21_R5; case "1.21.9-R0.1-SNAPSHOT", "1.21.10-R0.1-SNAPSHOT" -> Version.V1_21_R6; case "1.21.11-R0.1-SNAPSHOT" -> Version.V1_21_R7; - case "26.1.2-R0.1-SNAPSHOT", "26.1.2", "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R1; + case "26.1.2-R0.1-SNAPSHOT", "26.1.2" -> Version.V26_R1; + case "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R2; default -> throw new IllegalStateException("Unexpected version: " + version); }; } else diff --git a/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java b/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java deleted file mode 100644 index abb4ca81..00000000 --- a/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package studio.magemonkey.codex.compat; - -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Method; - -class VersionManagerTest { - - @Test - void getPackageFromVersion_supportsPurpur2612() throws Exception { - Method method = VersionManager.class.getDeclaredMethod("getPackageFromVersion", String.class); - method.setAccessible(true); - - Object packageName = method.invoke(null, "26.1.2"); - assert packageName.equals("v26_2"); - } -} From 6473315ee2cb61162c3f7d89d2b4b40dde8f1673 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:55:52 +0200 Subject: [PATCH 4/6] nms package vor 26_1 --- codex-core/pom.xml | 5 + codex-nms/codex-nms-v26_1/pom.xml | 61 ++++ .../codex/nms/v26_1/ArmorUtilImpl.java | 72 ++++ .../codex/nms/v26_1/CompatImpl.java | 100 +++++ .../magemonkey/codex/nms/v26_1/NMSImpl.java | 342 ++++++++++++++++++ codex-nms/pom.xml | 1 + 6 files changed, 581 insertions(+) create mode 100644 codex-nms/codex-nms-v26_1/pom.xml create mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java create mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java create mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java diff --git a/codex-core/pom.xml b/codex-core/pom.xml index 6cc3bee9..44522d82 100644 --- a/codex-core/pom.xml +++ b/codex-core/pom.xml @@ -101,6 +101,11 @@ codex-nms-v1_21_11 ${nms.version} + + studio.magemonkey + codex-nms-v26_1 + ${nms.version} + studio.magemonkey codex-nms-v26_2 diff --git a/codex-nms/codex-nms-v26_1/pom.xml b/codex-nms/codex-nms-v26_1/pom.xml new file mode 100644 index 00000000..648cd35b --- /dev/null +++ b/codex-nms/codex-nms-v26_1/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + studio.magemonkey + codex-nms + 1.2.0-R0.1-SNAPSHOT + + + codex-nms-v26_1 + CodexNMS-26.1 + 26.1 NMS utilities for Codex + + + 21 + + + + + org.spigotmc + spigot-api + 26.1.2-R0.1-SNAPSHOT + provided + + + org.spigotmc + spigot + 26.1.2-R0.1-SNAPSHOT + provided + + + it.unimi.dsi + fastutil + 8.5.18 + provided + + + + + + MIT License + https://www.opensource.org/licenses/mit-license.php + + + + + + VoidEdge + VoidEdge + https://void.travja.dev + + + + + scm:git:git://github.com/magemonkeystudio/codex.git + scm:git:ssh://github.com:magemonkeystudio/codex.git + https://github.com/magemonkeystudio/codex + + diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java new file mode 100644 index 00000000..74f5777c --- /dev/null +++ b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java @@ -0,0 +1,72 @@ +package studio.magemonkey.codex.nms.v26_1; + +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.inventory.meta.ArmorMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.trim.ArmorTrim; +import org.bukkit.inventory.meta.trim.TrimMaterial; +import org.bukkit.inventory.meta.trim.TrimPattern; +import studio.magemonkey.codex.compat.ArmorUtil; +import studio.magemonkey.codex.util.random.Rnd; + +import java.util.Objects; + +@SuppressWarnings("UnstableApiUsage") +public class ArmorUtilImpl implements ArmorUtil { + @Override + public Object getTrimMaterial(NamespacedKey key) { + return Registry.TRIM_MATERIAL.get(key); + } + + @Override + public Object getTrimPattern(NamespacedKey key) { + return Registry.TRIM_PATTERN.get(key); + } + + @Override + public void addTrim(ItemMeta meta, String material, String pattern) { + if (!(meta instanceof ArmorMeta)) return; + ArmorMeta armorMeta = (ArmorMeta) meta; + + TrimMaterial trimMaterial = null; + if (material.equals("*")) { + int size = 0; + for (TrimMaterial ignored : Registry.TRIM_MATERIAL) { + size++; + } + int index = Rnd.get(size); + int i = 0; + for (TrimMaterial next : Registry.TRIM_MATERIAL) { + if (index == i) { + trimMaterial = next; + break; + } + i++; + } + } else { + trimMaterial = Registry.TRIM_MATERIAL.get(NamespacedKey.minecraft(material)); + } + + TrimPattern trimPattern = null; + if (pattern.equals("*")) { + int size = 0; + for (TrimPattern ignored : Registry.TRIM_PATTERN) size++; + + int index = Rnd.get(size); + int i = 0; + for (TrimPattern next : Registry.TRIM_PATTERN) { + if (index == i++) { + trimPattern = next; + break; + } + } + } else { + trimPattern = Registry.TRIM_PATTERN.get(NamespacedKey.minecraft(pattern)); + } + + ArmorTrim armorTrim = + new ArmorTrim(Objects.requireNonNull(trimMaterial), Objects.requireNonNull(trimPattern)); + armorMeta.setTrim(armorTrim); + } +} diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java new file mode 100644 index 00000000..8441b7ef --- /dev/null +++ b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java @@ -0,0 +1,100 @@ +package studio.magemonkey.codex.nms.v26_1; + +import net.minecraft.locale.Language; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryEvent; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import studio.magemonkey.codex.api.meta.NBTAttribute; +import studio.magemonkey.codex.compat.Compat; + +public class CompatImpl implements Compat { + @Override + @SuppressWarnings("UnstableApiUsage") + public AttributeModifier createAttributeModifier(NBTAttribute attribute, + double amount, + AttributeModifier.Operation operation) { + return new AttributeModifier(attribute.getAttribute().getKey(), amount, operation, EquipmentSlotGroup.ANY); + } + + @Override + public String getAttributeKey(AttributeModifier attributeModifier) { + return attributeModifier.getKey().toString(); + } + + @Override + public String getAttributeKey(NBTAttribute attribute) { + return attribute.getAttribute().getKey().toString(); + } + + @Override + public Inventory getTopInventory(InventoryEvent event) { + InventoryView view = event.getView(); + return view.getTopInventory(); + } + + @Override + @NotNull + public Inventory getTopInventory(Player player) { + InventoryView view = player.getOpenInventory(); + return view.getTopInventory(); + } + + @Override + public Inventory getBottomInventory(InventoryEvent event) { + InventoryView view = event.getView(); + return view.getBottomInventory(); + } + + @Override + public void setCursor(InventoryEvent event, ItemStack item) { + InventoryView view = event.getView(); + view.setCursor(item); + } + + @Override + public void setItem(Player player, int slot, ItemStack item) { + InventoryView view = player.getOpenInventory(); + view.setItem(slot, item); + } + + @Override + public Inventory getInventory(InventoryEvent event, int slot) { + InventoryView view = event.getView(); + return view.getInventory(slot); + } + + @Override + public ItemStack getItem(InventoryEvent event, int slot) { + InventoryView view = event.getView(); + return view.getItem(slot); + } + + @Override + public int convertSlot(InventoryEvent event, int slot) { + InventoryView view = event.getView(); + return view.convertSlot(slot); + } + + @Override + public String getItemName(ItemStack item) { + ItemMeta meta = item.getItemMeta(); + if (meta == null) { + return item.getType().toString(); + } + + String name = null; + + if (meta.hasDisplayName()) name = meta.getDisplayName(); + if (name == null && meta.hasItemName()) name = meta.getItemName(); + if (name == null && meta.getLore() != null && !meta.getLore().isEmpty()) name = meta.getLore().get(0); + if (name == null) name = Language.getInstance().getOrDefault(item.getTranslationKey()); + + return name == null ? item.getType().toString() : name; + } +} diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java new file mode 100644 index 00000000..953b89fc --- /dev/null +++ b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java @@ -0,0 +1,342 @@ +package studio.magemonkey.codex.nms.v26_1; + +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.netty.channel.Channel; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; +import net.md_5.bungee.api.chat.hover.content.Content; +import net.minecraft.core.BlockPos; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundAnimatePacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.AxeItem; +import net.minecraft.world.item.TridentItem; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.attribute.Attribute; +import org.bukkit.block.Block; +import org.bukkit.block.Skull; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.damage.DamageSource; +import org.bukkit.damage.DamageType; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ArmorMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; +import org.bukkit.profile.PlayerTextures; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Scoreboard; +import org.jetbrains.annotations.NotNull; +import studio.magemonkey.codex.Codex; +import studio.magemonkey.codex.compat.NMS; + +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Base64; +import java.util.Locale; +import java.util.UUID; + +public class NMSImpl implements NMS { + @Override + public String getVersion() { + return "26.2"; + } + + @NotNull + @Override + public Object getConnection(Player player) { + return ((CraftPlayer) player).getHandle().connection; + } + + @NotNull + @Override + public Channel getChannel(@NotNull Player player) { + ServerGamePacketListenerImpl connection = ((ServerGamePacketListenerImpl) getConnection(player)); + // We have to reflexively get the `channel` field + try { + Field networkManagerField = getField(connection.getClass(), "connection"); + networkManagerField.setAccessible(true); + Connection networkManager = (Connection) networkManagerField.get(connection); + return networkManager.channel; + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException("Could not get Channel field from PlayerConnection", e); + } + } + + @Override + public void sendPacket(@NotNull Player player, @NotNull Object packet) { + Preconditions.checkArgument(packet instanceof Packet, + "Packet must be an instance of net.minecraft.server.Packet"); + ((ServerGamePacketListenerImpl) getConnection(player)).send((Packet) packet); + } + + @Override + public void openChestAnimation(@NotNull Block chest, boolean open) { + ServerLevel world = ((CraftWorld) chest.getWorld()).getHandle(); + BlockPos position = new BlockPos(chest.getX(), chest.getY(), chest.getZ()); + BlockState blockData = world.getBlockState(position); + world.blockEvent(position, blockData.getBlock(), 1, open ? 1 : 0); + } + + @Override + public void sendAttackPacket(@NotNull Player player, int i) { + ClientboundAnimatePacket packet = new ClientboundAnimatePacket(((CraftPlayer) player).getHandle(), i); + sendPacket(player, packet); + } + + @NotNull + @Override + public String fixColors(@NotNull String str) { + str = str.replace("\n", "%n%"); + + Component baseComponent = CraftChatMessage.fromStringOrNull(str); + String singleColor = CraftChatMessage.fromComponent(baseComponent); + + return singleColor.replace("%n%", "\n"); + } + + @Override + public double getDefaultDamage(@NotNull ItemStack itemStack) { + return getAttributeValue(itemStack, Attribute.ATTACK_DAMAGE); + } + + @Override + public double getDefaultSpeed(@NotNull ItemStack itemStack) { + return getAttributeValue(itemStack, Attribute.ATTACK_SPEED); + } + + @Override + public double getDefaultArmor(@NotNull ItemStack itemStack) { + return getAttributeValue(itemStack, Attribute.ARMOR); + } + + @Override + public double getDefaultToughness(@NotNull ItemStack itemStack) { + return getAttributeValue(itemStack, Attribute.ARMOR_TOUGHNESS); + } + + @Override + public boolean isWeapon(@NotNull ItemStack itemStack) { + net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + Item item = nmsItem.getItem(); + return itemStack.getType().name().contains("SWORD") || item instanceof AxeItem || item instanceof TridentItem; + } + + @Override + public boolean isArmor(@NotNull ItemStack itemStack) { + return itemStack.getItemMeta() instanceof ArmorMeta; + } + + @Override + public boolean isTool(@NotNull ItemStack itemStack) { + Material material = itemStack.getType(); + String[] suffixes = {"_AXE", "_HOE", "_SHOVEL", "_PICKAXE", "_SHEARS"}; + return Arrays.stream(suffixes).anyMatch(suffix -> material.name().endsWith(suffix)); + } + + @Override + public String toJson(@NotNull ItemStack item) { + return "unsupported in 26.2"; + } + + @Override + public void setKiller(@NotNull LivingEntity entity, @NotNull Player killer) { + net.minecraft.world.entity.LivingEntity hit = ((CraftLivingEntity) entity).getHandle(); + hit.setLastHurtByPlayer(((CraftPlayer) killer).getHandle(), 100); + } + + @Override + public void changeSkull(@NotNull Block block, @NotNull String hash) { + try { + if (!(block.getState() instanceof Skull)) return; + + Skull skull = (Skull) block.getState(); + PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), hash.substring(0, 16)); + PlayerTextures textures = profile.getTextures(); + + // If the hash is a URL, we can just set the skin directly, otherwise, we need to decode + // the hash from Base64 and extract the url from the JSON data. + if (hash.startsWith("http")) { + textures.setSkin(new URL(hash)); + } else { + String decoded; + try { + decoded = new String(Base64.getDecoder().decode(hash)); + } catch (IllegalArgumentException ignored) { + try { + decoded = new String(Base64.getMimeDecoder().decode(hash)); + } catch (IllegalArgumentException ignored2) { + decoded = new String(Base64.getUrlDecoder().decode(hash)); + // If we throw again... we'll just break out + } + } + // Construct the json object + JsonObject json = new Gson().fromJson(decoded, JsonObject.class); + // Get the textures object + JsonObject texturesJson = json.getAsJsonObject("textures"); + // Get the skin object + JsonObject skin = texturesJson.getAsJsonObject("SKIN"); + // Get the url + String url = skin.get("url").getAsString(); + textures.setSkin(new URL(url)); + } + + profile = profile.update().get(); + skull.setOwnerProfile(profile); + skull.update(); + } catch (Exception e) { + Codex.warn("Could not update skull"); + e.printStackTrace(); + } + } + + @Override + public void addSkullTexture(@NotNull ItemStack item, @NotNull String value, @NotNull UUID uuid) { + if (item.getType() != Material.PLAYER_HEAD) return; + + SkullMeta meta = (SkullMeta) item.getItemMeta(); + if (meta == null) return; + + try { + PlayerProfile playerProfile = Bukkit.createPlayerProfile(uuid, uuid.toString().substring(0, 16)); + String decoded = new String(Base64.getDecoder().decode(value)); + JsonObject json = new Gson().fromJson(decoded, JsonObject.class); + JsonObject texturesJson = json.getAsJsonObject("textures"); + JsonObject skin = texturesJson.getAsJsonObject("SKIN"); + String url = skin.get("url").getAsString(); + playerProfile.getTextures().setSkin(new URL(url)); + meta.setOwnerProfile(playerProfile); + item.setItemMeta(meta); + } catch (MalformedURLException | NoClassDefFoundError | NoSuchMethodError | IllegalArgumentException e) { + NMS.super.addSkullTexture(item, value, uuid); + } + } + + @NotNull + @Override + public Attribute getAttribute(String name) { + return Registry.ATTRIBUTE.getOrThrow(NamespacedKey.minecraft(name.toLowerCase(Locale.US))); + } + + @Override + public Object getNMSCopy(@NotNull ItemStack item) { + return CraftItemStack.asNMSCopy(item); + } + + @Override + public Material getMaterial(Boat boat) { + String boatClassName = boat.getClass().getSimpleName().replace("Craft", ""); + // Split on capital letters and join with underscores + // ex: AcaciaBoat -> ACACIA_BOAT + String materialName = String.join("_", boatClassName.split("(?=[A-Z])")).toUpperCase(Locale.US); + Material mat = Material.matchMaterial(materialName); + + if (mat == null) mat = Material.OAK_BOAT; + + return mat; + } + + @Override + public Objective registerNewObjective(Scoreboard scoreboard, Objective objective) { + return scoreboard.registerNewObjective(objective.getName(), + objective.getTrackedCriteria(), + objective.getDisplayName()); + } + + @Override + @SuppressWarnings("deprecation") + public HoverEvent getHoverEvent(@NotNull ItemStack itemStack) { + String components = itemStack.getItemMeta() != null ? itemStack.getItemMeta().getAsString() : "{}"; + components = components.replaceAll(": ?0b", ": false") + .replaceAll(": ?1b", ": true") + .replaceAll(": ?(\\d+\\.\\d+)d", ": $1") + .replaceAll("\\b(\\d+\\.\\d+)f\\b", "$1"); + return new HoverEvent(HoverEvent.Action.SHOW_ITEM, + new ComponentsShowItem( + itemStack.getType().getKey().toString(), + itemStack.getAmount(), + new Gson().fromJson(components, JsonObject.class)) + ); + } + + /** + * Minimal show_item content carrying components for modern clients. + */ + private static final class ComponentsShowItem extends Content { + private final String id; + private final int count; + private final JsonElement components; + + private ComponentsShowItem(final String id, final int count, final JsonElement components) { + this.id = id; + this.count = count; + this.components = components; + } + + @Override + public HoverEvent.Action requiredAction() { + return HoverEvent.Action.SHOW_ITEM; + } + } + + @Override + public BaseComponent getTranslatedComponent(@NotNull ItemStack itemStack) { + ItemMeta meta = itemStack.getItemMeta(); + String string = null; + if (meta != null) { + string = meta.getDisplayName(); + if (string.isEmpty()) { + try { + string = meta.getItemName(); + } catch (NoSuchMethodError ignored) { + } + } + } + + BaseComponent baseComponent; + if (string == null || string.isEmpty()) { + baseComponent = new TranslatableComponent(itemStack.getType().getItemTranslationKey()); + } else { + baseComponent = new TextComponent(string); + } + + return baseComponent; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public EntityDamageByEntityEvent createEntityDamageEvent(@NotNull Entity entity, + @NotNull Entity attacker, + @NotNull EntityDamageByEntityEvent.DamageCause cause, + double damage) { + return new EntityDamageByEntityEvent(attacker, + entity, + cause, + DamageSource.builder(DamageType.MOB_ATTACK).build(), + damage); + } +} diff --git a/codex-nms/pom.xml b/codex-nms/pom.xml index 436559cc..d37a2865 100644 --- a/codex-nms/pom.xml +++ b/codex-nms/pom.xml @@ -43,6 +43,7 @@ codex-nms-v1_21_7 codex-nms-v1_21_10 codex-nms-v1_21_11 + codex-nms-v26_1 codex-nms-v26_2 From 739ee42f494c8448d00dca915bfe2ce0fc04fb99 Mon Sep 17 00:00:00 2001 From: MaksyKun <77341370+MaksyKun@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:56:00 +0200 Subject: [PATCH 5/6] build issue fix --- codex-nms/codex-nms-v1_21_11/pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/codex-nms/codex-nms-v1_21_11/pom.xml b/codex-nms/codex-nms-v1_21_11/pom.xml index b070547b..65f8699b 100644 --- a/codex-nms/codex-nms-v1_21_11/pom.xml +++ b/codex-nms/codex-nms-v1_21_11/pom.xml @@ -37,6 +37,18 @@ 8.5.18 provided + + com.mojang + datafixerupper + 9.0.19 + provided + + + com.mojang + brigadier + 1.3.10 + provided + From 0d90340410da312166398915ac9a73375132352f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 16:58:16 +0000 Subject: [PATCH 6/6] revert: undo requested commits fbf1ba6 6473315 739ee42 --- .../codex/compat/VersionManager.java | 3 +- .../studio/magemonkey/codex/core/Version.java | 6 +- .../codex/compat/VersionManagerTest.java | 17 + codex-core/pom.xml | 5 - codex-nms/codex-nms-v1_21_11/pom.xml | 12 - codex-nms/codex-nms-v26_1/pom.xml | 61 ---- .../codex/nms/v26_1/ArmorUtilImpl.java | 72 ---- .../codex/nms/v26_1/CompatImpl.java | 100 ----- .../magemonkey/codex/nms/v26_1/NMSImpl.java | 342 ------------------ codex-nms/pom.xml | 1 - 10 files changed, 20 insertions(+), 599 deletions(-) create mode 100644 codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java delete mode 100644 codex-nms/codex-nms-v26_1/pom.xml delete mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java delete mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java delete mode 100644 codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java diff --git a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java index 144db875..e0650c24 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/compat/VersionManager.java @@ -67,8 +67,7 @@ private static String getPackageFromVersion(String version) { case "1.21.6", "1.21.7", "1.21.8" -> "v1_21_7"; case "1.21.9", "1.21.10" -> "v1_21_10"; case "1.21.11" -> "v1_21_11"; - case "26.1.2" -> "v26_1"; - case "26.2" -> "v26_2"; + case "26.1.2", "26.2" -> "v26_2"; default -> throw new UnsupportedVersionException("Unknown version " + version); }; } diff --git a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java index b450de19..26534051 100644 --- a/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java +++ b/codex-api/src/main/java/studio/magemonkey/codex/core/Version.java @@ -26,8 +26,7 @@ public enum Version { V1_21_R5, V1_21_R6, V1_21_R7, - V26_R1, - V26_R2; + V26_R1; public static final Version CURRENT; @@ -57,8 +56,7 @@ else if (versionRaw.equals("craftbukkit")) { case "1.21.6-R0.1-SNAPSHOT", "1.21.7-R0.1-SNAPSHOT", "1.21.8-R0.1-SNAPSHOT" -> Version.V1_21_R5; case "1.21.9-R0.1-SNAPSHOT", "1.21.10-R0.1-SNAPSHOT" -> Version.V1_21_R6; case "1.21.11-R0.1-SNAPSHOT" -> Version.V1_21_R7; - case "26.1.2-R0.1-SNAPSHOT", "26.1.2" -> Version.V26_R1; - case "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R2; + case "26.1.2-R0.1-SNAPSHOT", "26.1.2", "26.2-R0.1-SNAPSHOT", "26.2" -> Version.V26_R1; default -> throw new IllegalStateException("Unexpected version: " + version); }; } else diff --git a/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java b/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java new file mode 100644 index 00000000..abb4ca81 --- /dev/null +++ b/codex-api/src/test/java/studio/magemonkey/codex/compat/VersionManagerTest.java @@ -0,0 +1,17 @@ +package studio.magemonkey.codex.compat; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +class VersionManagerTest { + + @Test + void getPackageFromVersion_supportsPurpur2612() throws Exception { + Method method = VersionManager.class.getDeclaredMethod("getPackageFromVersion", String.class); + method.setAccessible(true); + + Object packageName = method.invoke(null, "26.1.2"); + assert packageName.equals("v26_2"); + } +} diff --git a/codex-core/pom.xml b/codex-core/pom.xml index 44522d82..6cc3bee9 100644 --- a/codex-core/pom.xml +++ b/codex-core/pom.xml @@ -101,11 +101,6 @@ codex-nms-v1_21_11 ${nms.version} - - studio.magemonkey - codex-nms-v26_1 - ${nms.version} - studio.magemonkey codex-nms-v26_2 diff --git a/codex-nms/codex-nms-v1_21_11/pom.xml b/codex-nms/codex-nms-v1_21_11/pom.xml index 65f8699b..b070547b 100644 --- a/codex-nms/codex-nms-v1_21_11/pom.xml +++ b/codex-nms/codex-nms-v1_21_11/pom.xml @@ -37,18 +37,6 @@ 8.5.18 provided - - com.mojang - datafixerupper - 9.0.19 - provided - - - com.mojang - brigadier - 1.3.10 - provided - diff --git a/codex-nms/codex-nms-v26_1/pom.xml b/codex-nms/codex-nms-v26_1/pom.xml deleted file mode 100644 index 648cd35b..00000000 --- a/codex-nms/codex-nms-v26_1/pom.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - 4.0.0 - - studio.magemonkey - codex-nms - 1.2.0-R0.1-SNAPSHOT - - - codex-nms-v26_1 - CodexNMS-26.1 - 26.1 NMS utilities for Codex - - - 21 - - - - - org.spigotmc - spigot-api - 26.1.2-R0.1-SNAPSHOT - provided - - - org.spigotmc - spigot - 26.1.2-R0.1-SNAPSHOT - provided - - - it.unimi.dsi - fastutil - 8.5.18 - provided - - - - - - MIT License - https://www.opensource.org/licenses/mit-license.php - - - - - - VoidEdge - VoidEdge - https://void.travja.dev - - - - - scm:git:git://github.com/magemonkeystudio/codex.git - scm:git:ssh://github.com:magemonkeystudio/codex.git - https://github.com/magemonkeystudio/codex - - diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java deleted file mode 100644 index 74f5777c..00000000 --- a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/ArmorUtilImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -package studio.magemonkey.codex.nms.v26_1; - -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.inventory.meta.ArmorMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.trim.ArmorTrim; -import org.bukkit.inventory.meta.trim.TrimMaterial; -import org.bukkit.inventory.meta.trim.TrimPattern; -import studio.magemonkey.codex.compat.ArmorUtil; -import studio.magemonkey.codex.util.random.Rnd; - -import java.util.Objects; - -@SuppressWarnings("UnstableApiUsage") -public class ArmorUtilImpl implements ArmorUtil { - @Override - public Object getTrimMaterial(NamespacedKey key) { - return Registry.TRIM_MATERIAL.get(key); - } - - @Override - public Object getTrimPattern(NamespacedKey key) { - return Registry.TRIM_PATTERN.get(key); - } - - @Override - public void addTrim(ItemMeta meta, String material, String pattern) { - if (!(meta instanceof ArmorMeta)) return; - ArmorMeta armorMeta = (ArmorMeta) meta; - - TrimMaterial trimMaterial = null; - if (material.equals("*")) { - int size = 0; - for (TrimMaterial ignored : Registry.TRIM_MATERIAL) { - size++; - } - int index = Rnd.get(size); - int i = 0; - for (TrimMaterial next : Registry.TRIM_MATERIAL) { - if (index == i) { - trimMaterial = next; - break; - } - i++; - } - } else { - trimMaterial = Registry.TRIM_MATERIAL.get(NamespacedKey.minecraft(material)); - } - - TrimPattern trimPattern = null; - if (pattern.equals("*")) { - int size = 0; - for (TrimPattern ignored : Registry.TRIM_PATTERN) size++; - - int index = Rnd.get(size); - int i = 0; - for (TrimPattern next : Registry.TRIM_PATTERN) { - if (index == i++) { - trimPattern = next; - break; - } - } - } else { - trimPattern = Registry.TRIM_PATTERN.get(NamespacedKey.minecraft(pattern)); - } - - ArmorTrim armorTrim = - new ArmorTrim(Objects.requireNonNull(trimMaterial), Objects.requireNonNull(trimPattern)); - armorMeta.setTrim(armorTrim); - } -} diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java deleted file mode 100644 index 8441b7ef..00000000 --- a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/CompatImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -package studio.magemonkey.codex.nms.v26_1; - -import net.minecraft.locale.Language; -import org.bukkit.attribute.AttributeModifier; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryEvent; -import org.bukkit.inventory.EquipmentSlotGroup; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.jetbrains.annotations.NotNull; -import studio.magemonkey.codex.api.meta.NBTAttribute; -import studio.magemonkey.codex.compat.Compat; - -public class CompatImpl implements Compat { - @Override - @SuppressWarnings("UnstableApiUsage") - public AttributeModifier createAttributeModifier(NBTAttribute attribute, - double amount, - AttributeModifier.Operation operation) { - return new AttributeModifier(attribute.getAttribute().getKey(), amount, operation, EquipmentSlotGroup.ANY); - } - - @Override - public String getAttributeKey(AttributeModifier attributeModifier) { - return attributeModifier.getKey().toString(); - } - - @Override - public String getAttributeKey(NBTAttribute attribute) { - return attribute.getAttribute().getKey().toString(); - } - - @Override - public Inventory getTopInventory(InventoryEvent event) { - InventoryView view = event.getView(); - return view.getTopInventory(); - } - - @Override - @NotNull - public Inventory getTopInventory(Player player) { - InventoryView view = player.getOpenInventory(); - return view.getTopInventory(); - } - - @Override - public Inventory getBottomInventory(InventoryEvent event) { - InventoryView view = event.getView(); - return view.getBottomInventory(); - } - - @Override - public void setCursor(InventoryEvent event, ItemStack item) { - InventoryView view = event.getView(); - view.setCursor(item); - } - - @Override - public void setItem(Player player, int slot, ItemStack item) { - InventoryView view = player.getOpenInventory(); - view.setItem(slot, item); - } - - @Override - public Inventory getInventory(InventoryEvent event, int slot) { - InventoryView view = event.getView(); - return view.getInventory(slot); - } - - @Override - public ItemStack getItem(InventoryEvent event, int slot) { - InventoryView view = event.getView(); - return view.getItem(slot); - } - - @Override - public int convertSlot(InventoryEvent event, int slot) { - InventoryView view = event.getView(); - return view.convertSlot(slot); - } - - @Override - public String getItemName(ItemStack item) { - ItemMeta meta = item.getItemMeta(); - if (meta == null) { - return item.getType().toString(); - } - - String name = null; - - if (meta.hasDisplayName()) name = meta.getDisplayName(); - if (name == null && meta.hasItemName()) name = meta.getItemName(); - if (name == null && meta.getLore() != null && !meta.getLore().isEmpty()) name = meta.getLore().get(0); - if (name == null) name = Language.getInstance().getOrDefault(item.getTranslationKey()); - - return name == null ? item.getType().toString() : name; - } -} diff --git a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java b/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java deleted file mode 100644 index 953b89fc..00000000 --- a/codex-nms/codex-nms-v26_1/src/main/java/studio/magemonkey/codex/nms/v26_1/NMSImpl.java +++ /dev/null @@ -1,342 +0,0 @@ -package studio.magemonkey.codex.nms.v26_1; - -import com.google.common.base.Preconditions; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import io.netty.channel.Channel; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.chat.TranslatableComponent; -import net.md_5.bungee.api.chat.hover.content.Content; -import net.minecraft.core.BlockPos; -import net.minecraft.network.Connection; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundAnimatePacket; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.TridentItem; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Registry; -import org.bukkit.attribute.Attribute; -import org.bukkit.block.Block; -import org.bukkit.block.Skull; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.entity.CraftLivingEntity; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.craftbukkit.util.CraftChatMessage; -import org.bukkit.damage.DamageSource; -import org.bukkit.damage.DamageType; -import org.bukkit.entity.Boat; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ArmorMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; -import org.bukkit.profile.PlayerProfile; -import org.bukkit.profile.PlayerTextures; -import org.bukkit.scoreboard.Objective; -import org.bukkit.scoreboard.Scoreboard; -import org.jetbrains.annotations.NotNull; -import studio.magemonkey.codex.Codex; -import studio.magemonkey.codex.compat.NMS; - -import java.lang.reflect.Field; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.Base64; -import java.util.Locale; -import java.util.UUID; - -public class NMSImpl implements NMS { - @Override - public String getVersion() { - return "26.2"; - } - - @NotNull - @Override - public Object getConnection(Player player) { - return ((CraftPlayer) player).getHandle().connection; - } - - @NotNull - @Override - public Channel getChannel(@NotNull Player player) { - ServerGamePacketListenerImpl connection = ((ServerGamePacketListenerImpl) getConnection(player)); - // We have to reflexively get the `channel` field - try { - Field networkManagerField = getField(connection.getClass(), "connection"); - networkManagerField.setAccessible(true); - Connection networkManager = (Connection) networkManagerField.get(connection); - return networkManager.channel; - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException("Could not get Channel field from PlayerConnection", e); - } - } - - @Override - public void sendPacket(@NotNull Player player, @NotNull Object packet) { - Preconditions.checkArgument(packet instanceof Packet, - "Packet must be an instance of net.minecraft.server.Packet"); - ((ServerGamePacketListenerImpl) getConnection(player)).send((Packet) packet); - } - - @Override - public void openChestAnimation(@NotNull Block chest, boolean open) { - ServerLevel world = ((CraftWorld) chest.getWorld()).getHandle(); - BlockPos position = new BlockPos(chest.getX(), chest.getY(), chest.getZ()); - BlockState blockData = world.getBlockState(position); - world.blockEvent(position, blockData.getBlock(), 1, open ? 1 : 0); - } - - @Override - public void sendAttackPacket(@NotNull Player player, int i) { - ClientboundAnimatePacket packet = new ClientboundAnimatePacket(((CraftPlayer) player).getHandle(), i); - sendPacket(player, packet); - } - - @NotNull - @Override - public String fixColors(@NotNull String str) { - str = str.replace("\n", "%n%"); - - Component baseComponent = CraftChatMessage.fromStringOrNull(str); - String singleColor = CraftChatMessage.fromComponent(baseComponent); - - return singleColor.replace("%n%", "\n"); - } - - @Override - public double getDefaultDamage(@NotNull ItemStack itemStack) { - return getAttributeValue(itemStack, Attribute.ATTACK_DAMAGE); - } - - @Override - public double getDefaultSpeed(@NotNull ItemStack itemStack) { - return getAttributeValue(itemStack, Attribute.ATTACK_SPEED); - } - - @Override - public double getDefaultArmor(@NotNull ItemStack itemStack) { - return getAttributeValue(itemStack, Attribute.ARMOR); - } - - @Override - public double getDefaultToughness(@NotNull ItemStack itemStack) { - return getAttributeValue(itemStack, Attribute.ARMOR_TOUGHNESS); - } - - @Override - public boolean isWeapon(@NotNull ItemStack itemStack) { - net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); - Item item = nmsItem.getItem(); - return itemStack.getType().name().contains("SWORD") || item instanceof AxeItem || item instanceof TridentItem; - } - - @Override - public boolean isArmor(@NotNull ItemStack itemStack) { - return itemStack.getItemMeta() instanceof ArmorMeta; - } - - @Override - public boolean isTool(@NotNull ItemStack itemStack) { - Material material = itemStack.getType(); - String[] suffixes = {"_AXE", "_HOE", "_SHOVEL", "_PICKAXE", "_SHEARS"}; - return Arrays.stream(suffixes).anyMatch(suffix -> material.name().endsWith(suffix)); - } - - @Override - public String toJson(@NotNull ItemStack item) { - return "unsupported in 26.2"; - } - - @Override - public void setKiller(@NotNull LivingEntity entity, @NotNull Player killer) { - net.minecraft.world.entity.LivingEntity hit = ((CraftLivingEntity) entity).getHandle(); - hit.setLastHurtByPlayer(((CraftPlayer) killer).getHandle(), 100); - } - - @Override - public void changeSkull(@NotNull Block block, @NotNull String hash) { - try { - if (!(block.getState() instanceof Skull)) return; - - Skull skull = (Skull) block.getState(); - PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), hash.substring(0, 16)); - PlayerTextures textures = profile.getTextures(); - - // If the hash is a URL, we can just set the skin directly, otherwise, we need to decode - // the hash from Base64 and extract the url from the JSON data. - if (hash.startsWith("http")) { - textures.setSkin(new URL(hash)); - } else { - String decoded; - try { - decoded = new String(Base64.getDecoder().decode(hash)); - } catch (IllegalArgumentException ignored) { - try { - decoded = new String(Base64.getMimeDecoder().decode(hash)); - } catch (IllegalArgumentException ignored2) { - decoded = new String(Base64.getUrlDecoder().decode(hash)); - // If we throw again... we'll just break out - } - } - // Construct the json object - JsonObject json = new Gson().fromJson(decoded, JsonObject.class); - // Get the textures object - JsonObject texturesJson = json.getAsJsonObject("textures"); - // Get the skin object - JsonObject skin = texturesJson.getAsJsonObject("SKIN"); - // Get the url - String url = skin.get("url").getAsString(); - textures.setSkin(new URL(url)); - } - - profile = profile.update().get(); - skull.setOwnerProfile(profile); - skull.update(); - } catch (Exception e) { - Codex.warn("Could not update skull"); - e.printStackTrace(); - } - } - - @Override - public void addSkullTexture(@NotNull ItemStack item, @NotNull String value, @NotNull UUID uuid) { - if (item.getType() != Material.PLAYER_HEAD) return; - - SkullMeta meta = (SkullMeta) item.getItemMeta(); - if (meta == null) return; - - try { - PlayerProfile playerProfile = Bukkit.createPlayerProfile(uuid, uuid.toString().substring(0, 16)); - String decoded = new String(Base64.getDecoder().decode(value)); - JsonObject json = new Gson().fromJson(decoded, JsonObject.class); - JsonObject texturesJson = json.getAsJsonObject("textures"); - JsonObject skin = texturesJson.getAsJsonObject("SKIN"); - String url = skin.get("url").getAsString(); - playerProfile.getTextures().setSkin(new URL(url)); - meta.setOwnerProfile(playerProfile); - item.setItemMeta(meta); - } catch (MalformedURLException | NoClassDefFoundError | NoSuchMethodError | IllegalArgumentException e) { - NMS.super.addSkullTexture(item, value, uuid); - } - } - - @NotNull - @Override - public Attribute getAttribute(String name) { - return Registry.ATTRIBUTE.getOrThrow(NamespacedKey.minecraft(name.toLowerCase(Locale.US))); - } - - @Override - public Object getNMSCopy(@NotNull ItemStack item) { - return CraftItemStack.asNMSCopy(item); - } - - @Override - public Material getMaterial(Boat boat) { - String boatClassName = boat.getClass().getSimpleName().replace("Craft", ""); - // Split on capital letters and join with underscores - // ex: AcaciaBoat -> ACACIA_BOAT - String materialName = String.join("_", boatClassName.split("(?=[A-Z])")).toUpperCase(Locale.US); - Material mat = Material.matchMaterial(materialName); - - if (mat == null) mat = Material.OAK_BOAT; - - return mat; - } - - @Override - public Objective registerNewObjective(Scoreboard scoreboard, Objective objective) { - return scoreboard.registerNewObjective(objective.getName(), - objective.getTrackedCriteria(), - objective.getDisplayName()); - } - - @Override - @SuppressWarnings("deprecation") - public HoverEvent getHoverEvent(@NotNull ItemStack itemStack) { - String components = itemStack.getItemMeta() != null ? itemStack.getItemMeta().getAsString() : "{}"; - components = components.replaceAll(": ?0b", ": false") - .replaceAll(": ?1b", ": true") - .replaceAll(": ?(\\d+\\.\\d+)d", ": $1") - .replaceAll("\\b(\\d+\\.\\d+)f\\b", "$1"); - return new HoverEvent(HoverEvent.Action.SHOW_ITEM, - new ComponentsShowItem( - itemStack.getType().getKey().toString(), - itemStack.getAmount(), - new Gson().fromJson(components, JsonObject.class)) - ); - } - - /** - * Minimal show_item content carrying components for modern clients. - */ - private static final class ComponentsShowItem extends Content { - private final String id; - private final int count; - private final JsonElement components; - - private ComponentsShowItem(final String id, final int count, final JsonElement components) { - this.id = id; - this.count = count; - this.components = components; - } - - @Override - public HoverEvent.Action requiredAction() { - return HoverEvent.Action.SHOW_ITEM; - } - } - - @Override - public BaseComponent getTranslatedComponent(@NotNull ItemStack itemStack) { - ItemMeta meta = itemStack.getItemMeta(); - String string = null; - if (meta != null) { - string = meta.getDisplayName(); - if (string.isEmpty()) { - try { - string = meta.getItemName(); - } catch (NoSuchMethodError ignored) { - } - } - } - - BaseComponent baseComponent; - if (string == null || string.isEmpty()) { - baseComponent = new TranslatableComponent(itemStack.getType().getItemTranslationKey()); - } else { - baseComponent = new TextComponent(string); - } - - return baseComponent; - } - - @Override - @SuppressWarnings("UnstableApiUsage") - public EntityDamageByEntityEvent createEntityDamageEvent(@NotNull Entity entity, - @NotNull Entity attacker, - @NotNull EntityDamageByEntityEvent.DamageCause cause, - double damage) { - return new EntityDamageByEntityEvent(attacker, - entity, - cause, - DamageSource.builder(DamageType.MOB_ATTACK).build(), - damage); - } -} diff --git a/codex-nms/pom.xml b/codex-nms/pom.xml index d37a2865..436559cc 100644 --- a/codex-nms/pom.xml +++ b/codex-nms/pom.xml @@ -43,7 +43,6 @@ codex-nms-v1_21_7 codex-nms-v1_21_10 codex-nms-v1_21_11 - codex-nms-v26_1 codex-nms-v26_2