diff --git a/InvUI/pom.xml b/InvUI/pom.xml index 1face2c..23899b6 100644 --- a/InvUI/pom.xml +++ b/InvUI/pom.xml @@ -22,7 +22,7 @@ org.spigotmc spigot-api - 1.17-R0.1-SNAPSHOT + 1.18.1-R0.1-SNAPSHOT provided diff --git a/InvUI/src/main/java/de/studiocode/invui/resourcepack/ForceResourcePack.java b/InvUI/src/main/java/de/studiocode/invui/resourcepack/ForceResourcePack.java index bd448f0..63b60be 100644 --- a/InvUI/src/main/java/de/studiocode/invui/resourcepack/ForceResourcePack.java +++ b/InvUI/src/main/java/de/studiocode/invui/resourcepack/ForceResourcePack.java @@ -1,7 +1,11 @@ package de.studiocode.invui.resourcepack; import de.studiocode.inventoryaccess.util.ReflectionRegistry; +import de.studiocode.inventoryaccess.util.VersionUtils; +import de.studiocode.inventoryaccess.version.InventoryAccess; import de.studiocode.invui.InvUI; +import de.studiocode.invui.util.DataUtils; +import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -11,6 +15,9 @@ import org.bukkit.event.player.PlayerResourcePackStatusEvent; import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.Nullable; +import java.io.IOException; +import java.net.URL; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import static org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.DECLINED; @@ -21,6 +28,7 @@ import static org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.DECLI public class ForceResourcePack implements Listener { private static final String RP_VERSION = "v0.8"; + private static final ForceResourcePack INSTANCE = new ForceResourcePack(); /** * A resource pack with all the {@link Icon}s @@ -29,11 +37,11 @@ public class ForceResourcePack implements Listener { "https://github.com/NichtStudioCode/InvUIRP/releases/download/" + RP_VERSION + (ReflectionRegistry.VERSION > 14 ? "" : "-legacy") + "/InvUIRP.zip"; - private static final ForceResourcePack INSTANCE = new ForceResourcePack(); - private final HashMap tasks = new HashMap<>(); private String resourcePackUrl; + private BaseComponent[] prompt; + private byte[] hash; private ForceResourcePack() { Bukkit.getPluginManager().registerEvents(this, InvUI.getInstance().getPlugin()); @@ -52,10 +60,22 @@ public class ForceResourcePack implements Listener { * Can be set to null to stop forcing the Resource Pack. * * @param resourcePackUrl The ResourcePack URL String + * @param prompt The prompt to be displayed (since 1.17) */ - public void setResourcePackUrl(@Nullable String resourcePackUrl) { + public void setResourcePack(@Nullable String resourcePackUrl, @Nullable BaseComponent[] prompt) { this.resourcePackUrl = resourcePackUrl; - if (resourcePackUrl != null) Bukkit.getOnlinePlayers().forEach(this::sendResourcePack); + this.prompt = prompt; + + if (resourcePackUrl != null) { + try { + URL url = new URL(resourcePackUrl); + hash = DataUtils.createSha1Hash(url.openStream()); + } catch (IOException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + Bukkit.getOnlinePlayers().forEach(this::sendResourcePack); + } } @EventHandler @@ -64,9 +84,13 @@ public class ForceResourcePack implements Listener { } private void sendResourcePack(Player player) { - player.setResourcePack(resourcePackUrl); - tasks.put(player, Bukkit.getScheduler().runTaskLater(InvUI.getInstance().getPlugin(), - () -> kickPlayer(player), 20 * 5)); + if (VersionUtils.isServerHigherOrEqual("1.17.0")) { + InventoryAccess.getPlayerUtils().sendResourcePack(player, resourcePackUrl, hash, prompt, true); + } else { + player.setResourcePack(resourcePackUrl); + tasks.put(player, Bukkit.getScheduler().runTaskLater(InvUI.getInstance().getPlugin(), + () -> kickPlayer(player), 20 * 5)); + } } @EventHandler diff --git a/InvUI/src/main/java/de/studiocode/invui/resourcepack/Icon.java b/InvUI/src/main/java/de/studiocode/invui/resourcepack/Icon.java index 740e791..5099693 100644 --- a/InvUI/src/main/java/de/studiocode/invui/resourcepack/Icon.java +++ b/InvUI/src/main/java/de/studiocode/invui/resourcepack/Icon.java @@ -3,13 +3,14 @@ package de.studiocode.invui.resourcepack; import de.studiocode.invui.item.Item; import de.studiocode.invui.item.ItemBuilder; import de.studiocode.invui.item.impl.SimpleItem; +import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Material; /** * Custom-textured Items that look like they are a part of the inventory. * Needs the resource pack to work, activate ResourcePack forcing and * choose between the lightweight or complete version using - * {@link ForceResourcePack#setResourcePackUrl(String)}. + * {@link ForceResourcePack#setResourcePack(String, BaseComponent[])}. */ public enum Icon { diff --git a/InvUI/src/main/java/de/studiocode/invui/util/DataUtils.java b/InvUI/src/main/java/de/studiocode/invui/util/DataUtils.java index 1080ad6..d63c347 100644 --- a/InvUI/src/main/java/de/studiocode/invui/util/DataUtils.java +++ b/InvUI/src/main/java/de/studiocode/invui/util/DataUtils.java @@ -3,6 +3,9 @@ package de.studiocode.invui.util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.List; public class DataUtils { @@ -74,4 +77,15 @@ public class DataUtils { return array2d; } + public static byte[] createSha1Hash(InputStream in) throws NoSuchAlgorithmException, IOException { + MessageDigest md = MessageDigest.getInstance("SHA1"); + int len; + byte[] buffer = new byte[4096]; + while ((len = in.read(buffer)) != -1) { + md.update(buffer, 0, len); + } + in.close(); + return md.digest(); + } + } diff --git a/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/InventoryUtilsImpl.java b/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/InventoryUtilsImpl.java index 85cf74b..9ae8fc9 100644 --- a/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/InventoryUtilsImpl.java +++ b/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/InventoryUtilsImpl.java @@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull; public class InventoryUtilsImpl implements InventoryUtils { public static Component createNMSComponent(BaseComponent[] components) { + if (components == null) return null; + String json = ComponentSerializer.toString(components); return CraftChatMessage.fromJSON(json); } diff --git a/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/PlayerUtilsImpl.java b/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/PlayerUtilsImpl.java index 6fbd490..556d3c7 100644 --- a/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/PlayerUtilsImpl.java +++ b/InventoryAccess/IA-R6/src/main/java/de/studiocode/inventoryaccess/r6/util/PlayerUtilsImpl.java @@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r6.util; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapPatch; +import de.studiocode.inventoryaccess.util.DataUtils; import de.studiocode.inventoryaccess.util.ReflectionUtils; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; +import net.minecraft.network.protocol.game.ClientboundResourcePackPacket; import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.level.ServerPlayer; @@ -76,4 +79,16 @@ public class PlayerUtilsImpl implements PlayerUtils { ); } + @Override + public void sendResourcePack(@NotNull Player player, @NotNull String url, byte[] hash, @Nullable BaseComponent[] prompt, boolean force) { + var serverPlayer = ((CraftPlayer) player).getHandle(); + var packet = new ClientboundResourcePackPacket( + url, + DataUtils.toHexadecimalString(hash), + force, + InventoryUtilsImpl.createNMSComponent(prompt) + ); + serverPlayer.connection.connection.send(packet); + } + } \ No newline at end of file diff --git a/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/InventoryUtilsImpl.java b/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/InventoryUtilsImpl.java index e828efe..7fe0358 100644 --- a/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/InventoryUtilsImpl.java +++ b/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/InventoryUtilsImpl.java @@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull; public class InventoryUtilsImpl implements InventoryUtils { public static Component createNMSComponent(BaseComponent[] components) { + if (components == null) return null; + String json = ComponentSerializer.toString(components); return CraftChatMessage.fromJSON(json); } diff --git a/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/PlayerUtilsImpl.java b/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/PlayerUtilsImpl.java index b2d8e4b..ccfe73f 100644 --- a/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/PlayerUtilsImpl.java +++ b/InventoryAccess/IA-R7/src/main/java/de/studiocode/inventoryaccess/r7/util/PlayerUtilsImpl.java @@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r7.util; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapPatch; +import de.studiocode.inventoryaccess.util.DataUtils; import de.studiocode.inventoryaccess.util.ReflectionUtils; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; +import net.minecraft.network.protocol.game.ClientboundResourcePackPacket; import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.level.ServerPlayer; @@ -76,4 +79,16 @@ public class PlayerUtilsImpl implements PlayerUtils { ); } + @Override + public void sendResourcePack(@NotNull Player player, @NotNull String url, byte[] hash, @Nullable BaseComponent[] prompt, boolean force) { + var serverPlayer = ((CraftPlayer) player).getHandle(); + var packet = new ClientboundResourcePackPacket( + url, + DataUtils.toHexadecimalString(hash), + force, + InventoryUtilsImpl.createNMSComponent(prompt) + ); + serverPlayer.connection.connection.send(packet); + } + } diff --git a/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/InventoryUtilsImpl.java b/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/InventoryUtilsImpl.java index 99fea0b..7d65c06 100644 --- a/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/InventoryUtilsImpl.java +++ b/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/InventoryUtilsImpl.java @@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull; public class InventoryUtilsImpl implements InventoryUtils { public static Component createNMSComponent(BaseComponent[] components) { + if (components == null) return null; + String json = ComponentSerializer.toString(components); return CraftChatMessage.fromJSON(json); } diff --git a/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/PlayerUtilsImpl.java b/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/PlayerUtilsImpl.java index 85fdc99..c7ca223 100644 --- a/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/PlayerUtilsImpl.java +++ b/InventoryAccess/IA-R8/src/main/java/de/studiocode/inventoryaccess/r8/util/PlayerUtilsImpl.java @@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r8.util; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapPatch; +import de.studiocode.inventoryaccess.util.DataUtils; import de.studiocode.inventoryaccess.util.ReflectionUtils; +import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket; +import net.minecraft.network.protocol.game.ClientboundResourcePackPacket; import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.level.ServerPlayer; @@ -76,4 +79,16 @@ public class PlayerUtilsImpl implements PlayerUtils { ); } + @Override + public void sendResourcePack(@NotNull Player player, @NotNull String url, byte[] hash, @Nullable BaseComponent[] prompt, boolean force) { + var serverPlayer = ((CraftPlayer) player).getHandle(); + var packet = new ClientboundResourcePackPacket( + url, + DataUtils.toHexadecimalString(hash), + force, + InventoryUtilsImpl.createNMSComponent(prompt) + ); + serverPlayer.connection.connection.send(packet); + } + } diff --git a/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/abstraction/util/PlayerUtils.java b/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/abstraction/util/PlayerUtils.java index fae062d..330c040 100644 --- a/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/abstraction/util/PlayerUtils.java +++ b/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/abstraction/util/PlayerUtils.java @@ -2,6 +2,7 @@ package de.studiocode.inventoryaccess.abstraction.util; import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapPatch; +import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,4 +52,17 @@ public interface PlayerUtils { */ void sendMapUpdate(@NotNull Player player, int mapId, byte scale, boolean locked, @Nullable MapPatch mapPatch, @Nullable List icons); + /** + * Sends a resource pack request to a {@link Player} + * + * @param player The {@link Player} to receive the resource pack request + * @param url The URL of the resource pack + * @param hash The SHA1 hash of the resource pack + * @param prompt The prompt message to be displayed (since 1.17) + * @param force If the {@link Player} should be forced to download the resource pack (since 1.17) + */ + default void sendResourcePack(@NotNull Player player, @NotNull String url, byte[] hash, @Nullable BaseComponent[] prompt, boolean force) { + player.setResourcePack(url, hash); + } + } diff --git a/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/util/DataUtils.java b/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/util/DataUtils.java new file mode 100644 index 0000000..08f1f8e --- /dev/null +++ b/InventoryAccess/IA/src/main/java/de/studiocode/inventoryaccess/util/DataUtils.java @@ -0,0 +1,17 @@ +package de.studiocode.inventoryaccess.util; + +public class DataUtils { + + private static final char[] HEX_ARRAY = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + public static String toHexadecimalString(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + +}