Added support for 1.17's force resource pack prompt

This commit is contained in:
NichtStudioCode 2021-12-18 15:15:12 +01:00
parent cac8ace852
commit d35355f64b
12 changed files with 130 additions and 9 deletions

@ -22,7 +22,7 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version> <version>1.18.1-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

@ -1,7 +1,11 @@
package de.studiocode.invui.resourcepack; package de.studiocode.invui.resourcepack;
import de.studiocode.inventoryaccess.util.ReflectionRegistry; 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.InvUI;
import de.studiocode.invui.util.DataUtils;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -11,6 +15,9 @@ import org.bukkit.event.player.PlayerResourcePackStatusEvent;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
import static org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.DECLINED; 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 { public class ForceResourcePack implements Listener {
private static final String RP_VERSION = "v0.8"; private static final String RP_VERSION = "v0.8";
private static final ForceResourcePack INSTANCE = new ForceResourcePack();
/** /**
* A resource pack with all the {@link Icon}s * 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/" "https://github.com/NichtStudioCode/InvUIRP/releases/download/"
+ RP_VERSION + (ReflectionRegistry.VERSION > 14 ? "" : "-legacy") + "/InvUIRP.zip"; + RP_VERSION + (ReflectionRegistry.VERSION > 14 ? "" : "-legacy") + "/InvUIRP.zip";
private static final ForceResourcePack INSTANCE = new ForceResourcePack();
private final HashMap<Player, BukkitTask> tasks = new HashMap<>(); private final HashMap<Player, BukkitTask> tasks = new HashMap<>();
private String resourcePackUrl; private String resourcePackUrl;
private BaseComponent[] prompt;
private byte[] hash;
private ForceResourcePack() { private ForceResourcePack() {
Bukkit.getPluginManager().registerEvents(this, InvUI.getInstance().getPlugin()); 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. * Can be set to null to stop forcing the Resource Pack.
* *
* @param resourcePackUrl The ResourcePack URL String * @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; 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 @EventHandler
@ -64,10 +84,14 @@ public class ForceResourcePack implements Listener {
} }
private void sendResourcePack(Player player) { private void sendResourcePack(Player player) {
if (VersionUtils.isServerHigherOrEqual("1.17.0")) {
InventoryAccess.getPlayerUtils().sendResourcePack(player, resourcePackUrl, hash, prompt, true);
} else {
player.setResourcePack(resourcePackUrl); player.setResourcePack(resourcePackUrl);
tasks.put(player, Bukkit.getScheduler().runTaskLater(InvUI.getInstance().getPlugin(), tasks.put(player, Bukkit.getScheduler().runTaskLater(InvUI.getInstance().getPlugin(),
() -> kickPlayer(player), 20 * 5)); () -> kickPlayer(player), 20 * 5));
} }
}
@EventHandler @EventHandler
public void handleResourcePackStatus(PlayerResourcePackStatusEvent event) { public void handleResourcePackStatus(PlayerResourcePackStatusEvent event) {

@ -3,13 +3,14 @@ package de.studiocode.invui.resourcepack;
import de.studiocode.invui.item.Item; import de.studiocode.invui.item.Item;
import de.studiocode.invui.item.ItemBuilder; import de.studiocode.invui.item.ItemBuilder;
import de.studiocode.invui.item.impl.SimpleItem; import de.studiocode.invui.item.impl.SimpleItem;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.Material; import org.bukkit.Material;
/** /**
* Custom-textured Items that look like they are a part of the inventory. * Custom-textured Items that look like they are a part of the inventory.
* Needs the resource pack to work, activate ResourcePack forcing and * Needs the resource pack to work, activate ResourcePack forcing and
* choose between the lightweight or complete version using * choose between the lightweight or complete version using
* {@link ForceResourcePack#setResourcePackUrl(String)}. * {@link ForceResourcePack#setResourcePack(String, BaseComponent[])}.
*/ */
public enum Icon { public enum Icon {

@ -3,6 +3,9 @@ package de.studiocode.invui.util;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
public class DataUtils { public class DataUtils {
@ -74,4 +77,15 @@ public class DataUtils {
return array2d; 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();
}
} }

@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
public class InventoryUtilsImpl implements InventoryUtils { public class InventoryUtilsImpl implements InventoryUtils {
public static Component createNMSComponent(BaseComponent[] components) { public static Component createNMSComponent(BaseComponent[] components) {
if (components == null) return null;
String json = ComponentSerializer.toString(components); String json = ComponentSerializer.toString(components);
return CraftChatMessage.fromJSON(json); return CraftChatMessage.fromJSON(json);
} }

@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r6.util;
import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils;
import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapIcon;
import de.studiocode.inventoryaccess.map.MapPatch; import de.studiocode.inventoryaccess.map.MapPatch;
import de.studiocode.inventoryaccess.util.DataUtils;
import de.studiocode.inventoryaccess.util.ReflectionUtils; 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.ClientboundMapItemDataPacket;
import net.minecraft.network.protocol.game.ClientboundResourcePackPacket;
import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerPlayer; 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);
}
} }

@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
public class InventoryUtilsImpl implements InventoryUtils { public class InventoryUtilsImpl implements InventoryUtils {
public static Component createNMSComponent(BaseComponent[] components) { public static Component createNMSComponent(BaseComponent[] components) {
if (components == null) return null;
String json = ComponentSerializer.toString(components); String json = ComponentSerializer.toString(components);
return CraftChatMessage.fromJSON(json); return CraftChatMessage.fromJSON(json);
} }

@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r7.util;
import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils;
import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapIcon;
import de.studiocode.inventoryaccess.map.MapPatch; import de.studiocode.inventoryaccess.map.MapPatch;
import de.studiocode.inventoryaccess.util.DataUtils;
import de.studiocode.inventoryaccess.util.ReflectionUtils; 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.ClientboundMapItemDataPacket;
import net.minecraft.network.protocol.game.ClientboundResourcePackPacket;
import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerPlayer; 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);
}
} }

@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull;
public class InventoryUtilsImpl implements InventoryUtils { public class InventoryUtilsImpl implements InventoryUtils {
public static Component createNMSComponent(BaseComponent[] components) { public static Component createNMSComponent(BaseComponent[] components) {
if (components == null) return null;
String json = ComponentSerializer.toString(components); String json = ComponentSerializer.toString(components);
return CraftChatMessage.fromJSON(json); return CraftChatMessage.fromJSON(json);
} }

@ -3,8 +3,11 @@ package de.studiocode.inventoryaccess.r8.util;
import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils; import de.studiocode.inventoryaccess.abstraction.util.PlayerUtils;
import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapIcon;
import de.studiocode.inventoryaccess.map.MapPatch; import de.studiocode.inventoryaccess.map.MapPatch;
import de.studiocode.inventoryaccess.util.DataUtils;
import de.studiocode.inventoryaccess.util.ReflectionUtils; 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.ClientboundMapItemDataPacket;
import net.minecraft.network.protocol.game.ClientboundResourcePackPacket;
import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerPlayer; 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);
}
} }

@ -2,6 +2,7 @@ package de.studiocode.inventoryaccess.abstraction.util;
import de.studiocode.inventoryaccess.map.MapIcon; import de.studiocode.inventoryaccess.map.MapIcon;
import de.studiocode.inventoryaccess.map.MapPatch; import de.studiocode.inventoryaccess.map.MapPatch;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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<MapIcon> icons); void sendMapUpdate(@NotNull Player player, int mapId, byte scale, boolean locked, @Nullable MapPatch mapPatch, @Nullable List<MapIcon> 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);
}
} }

@ -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);
}
}