skin = Optional.of(future.get().getProfile().getBase64Texture());
- skins.put(contributor.getMinecraftName(), skin.orElse(""));
- return skin.orElse(null);
+ String texture = lookupTexture(uuid.get());
+ skins.put(contributor.getMinecraftName(), texture == null ? "" : texture);
+ return texture;
} else {
return null;
}
}
+ private @Nullable UUID lookupUuid(@Nonnull String username) throws IOException {
+ String body = fetch("https://api.mojang.com/users/profiles/minecraft/" + username);
+ if (body == null) {
+ return null;
+ }
+ Matcher m = UUID_PATTERN.matcher(body);
+ if (!m.find()) {
+ return null;
+ }
+ String raw = m.group(1);
+ String formatted = raw.substring(0, 8) + '-' + raw.substring(8, 12) + '-'
+ + raw.substring(12, 16) + '-' + raw.substring(16, 20) + '-'
+ + raw.substring(20);
+ return UUID.fromString(formatted);
+ }
+
+ private @Nullable String lookupTexture(@Nonnull UUID uuid) throws IOException {
+ String body = fetch("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid);
+ if (body == null) {
+ return null;
+ }
+ Matcher m = TEXTURE_VALUE_PATTERN.matcher(body);
+ return m.find() ? m.group(1) : null;
+ }
+
+ private @Nullable String fetch(@Nonnull String endpoint) throws IOException {
+ URL url = URI.create(endpoint).toURL();
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setConnectTimeout(10_000);
+ conn.setReadTimeout(10_000);
+ conn.setRequestProperty("User-Agent", "Slimefun4");
+ int status = conn.getResponseCode();
+ if (status == 429) {
+ throw new IOException("429 Too Many Requests");
+ }
+ if (status != 200) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
index a146a9f620..cf19988fe9 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
@@ -32,9 +32,7 @@
import io.github.thebusybiscuit.slimefun4.core.services.LocalizationService;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
-import net.md_5.bungee.api.ChatMessageType;
-import net.md_5.bungee.api.chat.BaseComponent;
-import net.md_5.bungee.api.chat.TextComponent;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
/**
* This is an abstract parent class of {@link LocalizationService}.
@@ -362,8 +360,7 @@ public void sendActionbarMessage(@Nonnull Player player, @Nonnull String key, bo
String prefix = addPrefix ? getChatPrefix() : "";
String message = ChatColors.color(prefix + getMessage(player, key));
- BaseComponent[] components = TextComponent.fromLegacyText(message);
- player.spigot().sendMessage(ChatMessageType.ACTION_BAR, components);
+ player.sendActionBar(LegacyComponentSerializer.legacySection().deserialize(message));
}
public void sendMessage(@Nonnull CommandSender recipient, @Nonnull String key) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java
index 1be851ba17..e265094e0e 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java
@@ -147,7 +147,7 @@ public class Slimefun extends JavaPlugin implements SlimefunAddon {
* This does not necessarily mean that it's the minimum version
* required to run Slimefun.
*/
- private static final int RECOMMENDED_JAVA_VERSION = 17;
+ private static final int RECOMMENDED_JAVA_VERSION = 25;
/**
* Our static instance of {@link Slimefun}.
@@ -523,9 +523,14 @@ private boolean isVersionUnsupported() {
return true;
}
- // Now check the actual Version of Minecraft
- int version = PaperLib.getMinecraftVersion();
- int patchVersion = PaperLib.getMinecraftPatchVersion();
+ // Now check the actual Version of Minecraft.
+ // PaperLib 1.0.8 assumes the legacy "1.x.y" scheme and returns the
+ // drop number (e.g. 1 for "26.1.2"), so we parse the Bukkit version
+ // ourselves to also support the year.drop.hotfix scheme introduced
+ // with Minecraft 26.
+ int[] parsed = parseBukkitVersion(Bukkit.getBukkitVersion());
+ int version = parsed[0];
+ int patchVersion = parsed[1];
if (version > 0) {
// Check all supported versions of Minecraft
@@ -537,7 +542,10 @@ private boolean isVersionUnsupported() {
}
// Looks like you are using an unsupported Minecraft Version
- StartupWarnings.invalidMinecraftVersion(getLogger(), version, getDescription().getVersion());
+ String versionDisplay = version >= 26
+ ? version + "." + patchVersion + ".x"
+ : "1." + version + ".x";
+ StartupWarnings.invalidMinecraftVersion(getLogger(), versionDisplay, getDescription().getVersion());
return true;
} else {
getLogger().log(Level.WARNING, "We could not determine the version of Minecraft you were using? ({0})", Bukkit.getVersion());
@@ -558,6 +566,41 @@ private boolean isVersionUnsupported() {
}
}
+ /**
+ * Parses the value returned by {@link Bukkit#getBukkitVersion()} into a
+ * {@code {major, patch}} pair that maps onto {@link MinecraftVersion}'s
+ * numeric fields.
+ *
+ * Handles both the legacy {@code "1.."} format and the new
+ * {@code ".."} format (Minecraft 26+). Returns
+ * {@code {-1, -1}} when the string cannot be parsed.
+ */
+ private static int[] parseBukkitVersion(@Nonnull String bukkitVersion) {
+ try {
+ String core = bukkitVersion.split("-", 2)[0];
+ String[] parts = core.split("\\.");
+
+ if (parts.length < 2) {
+ return new int[] { -1, -1 };
+ }
+
+ int major;
+ int patch;
+ if ("1".equals(parts[0])) {
+ // Legacy scheme: 1..
+ major = Integer.parseInt(parts[1]);
+ patch = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
+ } else {
+ // Year-based scheme: ..
+ major = Integer.parseInt(parts[0]);
+ patch = Integer.parseInt(parts[1]);
+ }
+ return new int[] { major, patch };
+ } catch (NumberFormatException ex) {
+ return new int[] { -1, -1 };
+ }
+ }
+
/**
* This private method gives us a {@link Collection} of every {@link MinecraftVersion}
* that Slimefun is compatible with (as a {@link String} representation).
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java
index 77b3a7479a..2925d56264 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/StartupWarnings.java
@@ -34,12 +34,12 @@ static void discourageCSCoreLib(Logger logger) {
}
@ParametersAreNonnullByDefault
- static void invalidMinecraftVersion(Logger logger, int majorVersion, String slimefunVersion) {
+ static void invalidMinecraftVersion(Logger logger, String versionDisplay, String slimefunVersion) {
logger.log(Level.SEVERE, BORDER);
logger.log(Level.SEVERE, PREFIX + "Slimefun was not installed correctly!");
logger.log(Level.SEVERE, PREFIX + "You are using the wrong version of Minecraft!");
logger.log(Level.SEVERE, PREFIX);
- logger.log(Level.SEVERE, PREFIX + "You are using Minecraft 1.{0}.x", majorVersion);
+ logger.log(Level.SEVERE, PREFIX + "You are using Minecraft {0}", versionDisplay);
logger.log(Level.SEVERE, PREFIX + "but Slimefun {0} requires you to be using", slimefunVersion);
logger.log(Level.SEVERE, PREFIX + "Minecraft {0}", String.join(" / ", Slimefun.getSupportedVersions()));
logger.log(Level.SEVERE, BORDER);
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
index 669327a9bf..ae14200f34 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
@@ -34,8 +34,6 @@
import io.github.bakedlibs.dough.common.CommonPatterns;
import io.github.bakedlibs.dough.items.CustomItemStack;
import io.github.bakedlibs.dough.items.ItemUtils;
-import io.github.bakedlibs.dough.skins.PlayerHead;
-import io.github.bakedlibs.dough.skins.PlayerSkin;
import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack;
@@ -49,6 +47,7 @@
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
+import io.github.thebusybiscuit.slimefun4.utils.PlayerSkinUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.papermc.lib.PaperLib;
@@ -880,11 +879,10 @@ protected void move(Block b, BlockFace face, Block block) {
block.setBlockData(blockData);
Slimefun.runSync(() -> {
- PlayerSkin skin = PlayerSkin.fromBase64(texture);
Material type = block.getType();
// Ensure that this Block is still a Player Head
if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) {
- PlayerHead.setSkin(block, skin, true);
+ PlayerSkinUtils.setBlockSkinFromBase64(block, texture, true);
}
});
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java
index 6bcbffd93b..9a0577e7f9 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java
@@ -21,7 +21,6 @@
import org.bukkit.inventory.EquipmentSlot;
import io.github.bakedlibs.dough.common.ChatColors;
-import io.github.bakedlibs.dough.skins.PlayerHead;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetProvider;
@@ -29,6 +28,7 @@
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
+import io.github.thebusybiscuit.slimefun4.utils.PlayerSkinUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
@@ -95,7 +95,7 @@ private void onRightClick(Player p, Block b, BlockFace face) {
Block block = b.getRelative(face);
block.setType(Material.PLAYER_HEAD);
- PlayerHead.setSkin(block, HeadTexture.MISSING_TEXTURE.getAsSkin(), true);
+ PlayerSkinUtils.setBlockSkinFromHash(block, HeadTexture.MISSING_TEXTURE.getUniqueId(), HeadTexture.MISSING_TEXTURE.getTexture(), true);
SoundEffect.DEBUG_FISH_CLICK_SOUND.playFor(p);
}, 2L);
} else if (BlockStorage.hasBlockInfo(b)) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java
index 4f79580831..ac720a9ce5 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java
@@ -8,11 +8,9 @@
import org.bukkit.Server;
import org.bukkit.block.Block;
-import io.github.bakedlibs.dough.skins.PlayerHead;
-import io.github.bakedlibs.dough.skins.PlayerSkin;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor;
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
-import io.papermc.lib.PaperLib;
+import io.github.thebusybiscuit.slimefun4.utils.PlayerSkinUtils;
/**
* This task is run whenever a {@link Capacitor} needs to update their texture.
@@ -75,10 +73,7 @@ public void run() {
}
private void setTexture(@Nonnull Block b, @Nonnull HeadTexture texture) {
- PlayerSkin skin = PlayerSkin.fromHashCode(texture.getUniqueId(), texture.getTexture());
- PlayerHead.setSkin(b, skin, false);
-
- PaperLib.getBlockState(b, false).getState().update(true, false);
+ PlayerSkinUtils.setBlockSkinFromHash(b, texture.getUniqueId(), texture.getTexture(), false);
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java
index 85e4c4bfe8..a709d5d397 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java
@@ -10,9 +10,7 @@
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.RadioactivityListener;
import io.github.thebusybiscuit.slimefun4.utils.RadiationUtils;
-import net.md_5.bungee.api.ChatMessageType;
-import net.md_5.bungee.api.chat.BaseComponent;
-import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
@@ -92,9 +90,7 @@ protected void onPlayerTick(Player p, PlayerProfile profile) {
String msg = Slimefun.getLocalization()
.getMessage(p, "actionbar.radiation")
.replace("%level%", "" + exposureLevelAfter);
- BaseComponent[] components =
- new ComponentBuilder().append(ChatColors.color(msg)).create();
- p.spigot().sendMessage(ChatMessageType.ACTION_BAR, components);
+ p.sendActionBar(LegacyComponentSerializer.legacySection().deserialize(ChatColors.color(msg)));
}
} else {
RadiationUtils.removeExposure(p, 1);
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ArmorStandUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ArmorStandUtils.java
index e2cf5ae10f..e7b661dfa6 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ArmorStandUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ArmorStandUtils.java
@@ -2,7 +2,6 @@
import javax.annotation.Nonnull;
-import io.papermc.lib.PaperLib;
import org.bukkit.Location;
import org.bukkit.entity.ArmorStand;
@@ -50,16 +49,6 @@ private ArmorStandUtils() {}
* @return The spawned {@link ArmorStand}
*/
public static @Nonnull ArmorStand spawnArmorStand(@Nonnull Location location) {
- // The consumer method was moved from World to RegionAccessor in 1.20.2
- // Due to this, we need to use a rubbish workaround to support 1.20.1 and below
- // This causes flicker on these versions which sucks but not sure a better way around this right now.
- if (PaperLib.getMinecraftVersion() < 20 ||
- (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() < 2)) {
- ArmorStand armorStand = location.getWorld().spawn(location, ArmorStand.class);
- setupArmorStand(armorStand);
- return armorStand;
- }
-
return location.getWorld().spawn(location, ArmorStand.class, ArmorStandUtils::setupArmorStand);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java
index a4caf14774..ac8fd59377 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java
@@ -9,7 +9,6 @@
import org.bukkit.inventory.ItemStack;
import io.github.bakedlibs.dough.common.CommonPatterns;
-import io.github.bakedlibs.dough.skins.PlayerSkin;
/**
* This enum holds all currently used Head textures in Slimefun.
@@ -163,8 +162,4 @@ public enum HeadTexture {
return SlimefunUtils.getCustomHead(getTexture());
}
- public @Nonnull PlayerSkin getAsSkin() {
- return PlayerSkin.fromHashCode(texture);
- }
-
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/PlayerSkinUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/PlayerSkinUtils.java
new file mode 100644
index 0000000000..e6ab1c7b2a
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/PlayerSkinUtils.java
@@ -0,0 +1,97 @@
+package io.github.thebusybiscuit.slimefun4.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.Skull;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.profile.PlayerProfile;
+import org.bukkit.profile.PlayerTextures;
+
+/**
+ * Drop-in replacement for the shaded dough {@code PlayerSkin} / {@code PlayerHead}
+ * helpers. The dough library's {@code CustomGameProfile} extends Mojang's
+ * {@code GameProfile}, which was made {@code final} in Minecraft 26.1.2,
+ * so any call into dough.skins crashes with {@link IncompatibleClassChangeError}.
+ * This utility talks to Paper's native {@link PlayerProfile} API instead.
+ */
+public final class PlayerSkinUtils {
+
+ private static final Pattern URL_PATTERN = Pattern.compile("\"url\"\\s*:\\s*\"([^\"]+)\"");
+ private static final UUID ZERO_UUID = new UUID(0L, 0L);
+ private static final String TEXTURE_URL_PREFIX = "http://textures.minecraft.net/texture/";
+
+ private PlayerSkinUtils() {}
+
+ public static @Nonnull String hashToBase64(@Nonnull String hash) {
+ String json = "{\"textures\":{\"SKIN\":{\"url\":\"" + TEXTURE_URL_PREFIX + hash + "\"}}}";
+ return Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private static @Nullable URL extractUrl(@Nonnull String base64) {
+ try {
+ String json = new String(Base64.getDecoder().decode(base64), StandardCharsets.UTF_8);
+ Matcher m = URL_PATTERN.matcher(json);
+ if (!m.find()) {
+ return null;
+ }
+ return new URI(m.group(1)).toURL();
+ } catch (IllegalArgumentException | URISyntaxException | java.net.MalformedURLException e) {
+ return null;
+ }
+ }
+
+ private static @Nonnull PlayerProfile buildProfile(@Nonnull UUID uuid, @Nonnull String base64) {
+ PlayerProfile profile = Bukkit.createPlayerProfile(uuid);
+ URL url = extractUrl(base64);
+ if (url != null) {
+ PlayerTextures textures = profile.getTextures();
+ textures.setSkin(url);
+ profile.setTextures(textures);
+ }
+ return profile;
+ }
+
+ public static @Nonnull ItemStack getItemStackFromBase64(@Nonnull String base64) {
+ ItemStack head = new ItemStack(Material.PLAYER_HEAD);
+ SkullMeta meta = (SkullMeta) head.getItemMeta();
+ if (meta != null) {
+ meta.setOwnerProfile(buildProfile(ZERO_UUID, base64));
+ head.setItemMeta(meta);
+ }
+ return head;
+ }
+
+ public static @Nonnull ItemStack getItemStackFromHash(@Nonnull String hash) {
+ return getItemStackFromBase64(hashToBase64(hash));
+ }
+
+ public static void setBlockSkinFromBase64(@Nonnull Block block, @Nonnull String base64, boolean sendUpdate) {
+ if (!(block.getState() instanceof Skull skull)) {
+ return;
+ }
+ skull.setOwnerProfile(buildProfile(ZERO_UUID, base64));
+ skull.update(true, sendUpdate);
+ }
+
+ public static void setBlockSkinFromHash(@Nonnull Block block, @Nonnull UUID uuid, @Nonnull String hash, boolean sendUpdate) {
+ if (!(block.getState() instanceof Skull skull)) {
+ return;
+ }
+ skull.setOwnerProfile(buildProfile(uuid, hashToBase64(hash)));
+ skull.update(true, sendUpdate);
+ }
+}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java
index ed5bdb9b4d..fbbb970cdb 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java
@@ -1,8 +1,6 @@
package io.github.thebusybiscuit.slimefun4.utils;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
@@ -29,8 +27,6 @@
import io.github.bakedlibs.dough.common.CommonPatterns;
import io.github.bakedlibs.dough.items.ItemMetaSnapshot;
-import io.github.bakedlibs.dough.skins.PlayerHead;
-import io.github.bakedlibs.dough.skins.PlayerSkin;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.events.SlimefunItemSpawnEvent;
import io.github.thebusybiscuit.slimefun4.api.exceptions.PrematureCodeException;
@@ -238,11 +234,10 @@ public static boolean isRadioactive(@Nullable ItemStack item) {
String base64 = texture;
if (CommonPatterns.HEXADECIMAL.matcher(texture).matches()) {
- base64 = Base64.getEncoder().encodeToString(("{\"textures\":{\"SKIN\":{\"url\":\"http://textures.minecraft.net/texture/" + texture + "\"}}}").getBytes(StandardCharsets.UTF_8));
+ base64 = PlayerSkinUtils.hashToBase64(texture);
}
- PlayerSkin skin = PlayerSkin.fromBase64(base64);
- return PlayerHead.getItemStack(skin);
+ return PlayerSkinUtils.getItemStackFromBase64(base64);
}
public static boolean containsSimilarItem(Inventory inventory, ItemStack item, boolean checkLore) {
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 5e5a3adbe5..ed54458981 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -9,7 +9,7 @@ description: Slimefun basically turns your entire Server into a FTB modpack with
# Technical settings
main: io.github.thebusybiscuit.slimefun4.implementation.Slimefun
-api-version: '1.16'
+api-version: '1.21'
# (Soft) dependencies of Slimefun, we hook into these plugins.
softdepend:
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestMinecraftVersion.java b/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestMinecraftVersion.java
index 74a2622cce..dde7dcd497 100644
--- a/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestMinecraftVersion.java
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestMinecraftVersion.java
@@ -100,4 +100,22 @@ void testLowestSupportedVersion() {
Assertions.assertThrows(IllegalArgumentException.class, () -> MinecraftVersion.UNIT_TEST.isAtLeast(MinecraftVersion.MINECRAFT_1_16));
}
+ @Test
+ @DisplayName("Test MINECRAFT_26_1 matches 26.1.x and is ordered after 1.21")
+ void testMinecraft26() {
+ // 26.1 should match major=26, patch=1
+ Assertions.assertTrue(MinecraftVersion.MINECRAFT_26_1.isMinecraftVersion(26, 1));
+ // 26.1.2 hotfix also falls in the 26.1.x range
+ Assertions.assertTrue(MinecraftVersion.MINECRAFT_26_1.isMinecraftVersion(26, 2));
+ // 26.0.x is before the 26.1 drop — must not match
+ Assertions.assertFalse(MinecraftVersion.MINECRAFT_26_1.isMinecraftVersion(26, 0));
+ // different major must not match
+ Assertions.assertFalse(MinecraftVersion.MINECRAFT_26_1.isMinecraftVersion(21, 0));
+
+ // ordinal ordering: 26.1 is newer than 1.21
+ Assertions.assertTrue(MinecraftVersion.MINECRAFT_26_1.isAtLeast(MinecraftVersion.MINECRAFT_1_21));
+ Assertions.assertFalse(MinecraftVersion.MINECRAFT_26_1.isBefore(MinecraftVersion.MINECRAFT_1_21));
+ Assertions.assertTrue(MinecraftVersion.MINECRAFT_1_21.isBefore(MinecraftVersion.MINECRAFT_26_1));
+ }
+
}