diff --git a/AnarchyExploitFixesFolia/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java b/AnarchyExploitFixesFolia/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java index b49b7c632..77f668ee4 100644 --- a/AnarchyExploitFixesFolia/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java +++ b/AnarchyExploitFixesFolia/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java @@ -1,36 +1,35 @@ package me.moomoo.anarchyexploitfixes.modules.patches; import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes; +import me.moomoo.anarchyexploitfixes.config.Config; import me.moomoo.anarchyexploitfixes.modules.AnarchyExploitFixesModule; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.block.ShulkerBox; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerKickEvent; -import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.BookMeta; +import org.jetbrains.annotations.Nullable; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import static me.moomoo.anarchyexploitfixes.utils.ItemUtils.isBook; -import static me.moomoo.anarchyexploitfixes.utils.ItemUtils.isShulkerBox; +import java.util.Iterator; public class BookBan implements AnarchyExploitFixesModule, Listener { - private final int maxByteSizePerPage; + private final MiniMessage miniMessage; + private final int maxBookSize, maxItemSize, maxInventorySize; public BookBan() { shouldEnable(); - this.maxByteSizePerPage = AnarchyExploitFixes.getConfiguration().getInt("patches.prevent-book-ban.max-byte-size-per-page", 255); + this.miniMessage = MiniMessage.miniMessage(); + Config config = AnarchyExploitFixes.getConfiguration(); + this.maxBookSize = config.getInt("patches.anti-book-ban.max-book-size", 8000); + this.maxItemSize = config.getInt("patches.anti-book-ban.max-item-size", 8260); + this.maxInventorySize = config.getInt("patches.anti-book-ban.max-inventory-size", 50674); } @Override @@ -51,7 +50,7 @@ public void enable() { @Override public boolean shouldEnable() { - return AnarchyExploitFixes.getConfiguration().getBoolean("patches.prevent-book-ban.enable", true); + return AnarchyExploitFixes.getConfiguration().getBoolean("patches.anti-book-ban.enable", true); } @Override @@ -59,54 +58,84 @@ public void disable() { HandlerList.unregisterAll(this); } - private void clearBooks(Player player) { - for (ItemStack itemInInventory : player.getInventory()) { - if (isBook(itemInInventory)) { - stripPages(itemInInventory); - } - else if (isShulkerBox(itemInInventory)) { - BlockStateMeta meta = (BlockStateMeta) itemInInventory.getItemMeta(); - ShulkerBox shulkerBox = (ShulkerBox) meta.getBlockState(); - - for (ItemStack itemInsideShulker : shulkerBox.getInventory()) { - if (isBook(itemInsideShulker)) { - stripPages(itemInsideShulker); - } - } - - shulkerBox.update(); - meta.setBlockState(shulkerBox); - itemInInventory.setItemMeta(meta); - } + private long getItemSize(@Nullable ItemStack stack) { + long byteSize = 0L; + if (stack == null) return byteSize; + + if ( + stack.hasItemMeta() + && stack.getItemMeta() instanceof BlockStateMeta blockStateMeta + && blockStateMeta.getBlockState() instanceof ShulkerBox shulkerBox + ) { + byteSize += getInventorySize(shulkerBox.getInventory().getContents()); } + + // The serialize as bytes function takes significantly longer to run because + // it tries to compress the file. Instead, we will just use the string length, + // even though that is not a great proxy. + byteSize += stack.serialize().toString().getBytes(StandardCharsets.UTF_8).length; + + return byteSize; } - private void stripPages(ItemStack book) { - BookMeta bookMeta = (BookMeta) book.getItemMeta(); + private long getInventorySize(ItemStack[] contents) { + long size = 0L; + for (ItemStack stack : contents) { + size += getItemSize(stack); + } + return size; + } - List newPages = new ArrayList<>(bookMeta.getPageCount()); + // Prevent players from creating big books + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBookEdit(PlayerEditBookEvent event) { + // Map using MiniMessage#serialize so there are no possible bypasses using large components. + final String serializedPages = String.join("", event.getNewBookMeta().pages().stream().map(miniMessage::serialize).toList()); + if ( + serializedPages.getBytes(StandardCharsets.UTF_8).length > maxBookSize + || serializedPages.getBytes(StandardCharsets.UTF_16).length > maxBookSize + ) { + event.setCancelled(true); + } + } - for (Component page : bookMeta.pages()) { - if (PlainTextComponentSerializer.plainText().serialize(page).getBytes(StandardCharsets.UTF_8).length <= maxByteSizePerPage) - newPages.add(page); + // Attempt to stop players from picking up large items + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerAttemptPickupItem(PlayerAttemptPickupItemEvent event) { + final long itemSize = getItemSize(event.getItem().getItemStack()); + if (itemSize > maxItemSize) { + event.setCancelled(true); + return; } - bookMeta.pages(newPages); - book.setItemMeta(bookMeta); + // Check if the total inventory size would be larger than the maximum inventory size + if (itemSize + getInventorySize(event.getPlayer().getInventory().getContents()) > maxInventorySize) { + event.setCancelled(true); + } + } + + private void sanitizeInventory(PlayerInventory inventory) { + Iterator invIterator = inventory.iterator(); + while (invIterator.hasNext()) { + if (getItemSize(invIterator.next()) > maxItemSize) { + invIterator.remove(); + } + } } - @EventHandler(priority = EventPriority.NORMAL) + // Attempt to clear off big items from affected players + @EventHandler(priority = EventPriority.LOW) private void onJoin(PlayerJoinEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.LOW) private void onLeave(PlayerQuitEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.LOW) private void onKick(PlayerKickEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } -} +} \ No newline at end of file diff --git a/AnarchyExploitFixesLegacy/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java b/AnarchyExploitFixesLegacy/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java index 40ae553dd..011081822 100644 --- a/AnarchyExploitFixesLegacy/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java +++ b/AnarchyExploitFixesLegacy/src/main/java/me/moomoo/anarchyexploitfixes/modules/patches/BookBan.java @@ -1,38 +1,36 @@ package me.moomoo.anarchyexploitfixes.modules.patches; import me.moomoo.anarchyexploitfixes.AnarchyExploitFixes; +import me.moomoo.anarchyexploitfixes.config.Config; import me.moomoo.anarchyexploitfixes.modules.AnarchyExploitFixesModule; import org.bukkit.block.ShulkerBox; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerKickEvent; -import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.ItemMeta; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import static me.moomoo.anarchyexploitfixes.utils.ItemUtils.isBook; -import static me.moomoo.anarchyexploitfixes.utils.ItemUtils.isShulkerBox; +import java.util.Iterator; public class BookBan implements AnarchyExploitFixesModule, Listener { - private final int maxByteSizePerPage; + private final int maxBookSize, maxItemSize, maxInventorySize; public BookBan() { shouldEnable(); - this.maxByteSizePerPage = AnarchyExploitFixes.getConfiguration().getInt("patches.prevent-book-ban.max-byte-size-per-page", 255); + Config config = AnarchyExploitFixes.getConfiguration(); + this.maxBookSize = config.getInt("patches.anti-book-ban.max-book-size", 8000); + this.maxItemSize = config.getInt("patches.anti-book-ban.max-item-size", 8260); + this.maxInventorySize = config.getInt("patches.anti-book-ban.max-inventory-size", 50674); } @Override public String name() { - return "prevent-book-ban"; + return "anti-book-ban"; } @Override @@ -48,56 +46,88 @@ public void enable() { @Override public boolean shouldEnable() { - return AnarchyExploitFixes.getConfiguration().getBoolean("patches.prevent-book-ban.enable", true); + return AnarchyExploitFixes.getConfiguration().getBoolean("patches.anti-book-ban.enable", true); } - private void clearBooks(Player player) { - for (ItemStack itemInInventory : player.getInventory()) { - if (isBook(itemInInventory)) { - stripPages(itemInInventory); - } else if (isShulkerBox(itemInInventory)) { - BlockStateMeta meta = (BlockStateMeta) itemInInventory.getItemMeta(); - ShulkerBox shulkerBox = (ShulkerBox) meta.getBlockState(); - - for (ItemStack itemInsideShulker : shulkerBox.getInventory()) { - if (isBook(itemInsideShulker)) { - stripPages(itemInsideShulker); - } - } + private long getItemSize(ItemStack stack) { + long byteSize = 0L; + if (stack == null) return byteSize; - shulkerBox.update(); - meta.setBlockState(shulkerBox); - itemInInventory.setItemMeta(meta); + if (stack.hasItemMeta()) { + final ItemMeta itemMeta = stack.getItemMeta(); + if (itemMeta instanceof BlockStateMeta) { + final BlockStateMeta blockStateMeta = (BlockStateMeta) itemMeta; + if (blockStateMeta instanceof ShulkerBox) { + byteSize += getInventorySize(((ShulkerBox) blockStateMeta).getInventory().getContents()); + } } } + + // The serialize as bytes function takes significantly longer to run because + // it tries to compress the file. Instead, we will just use the string length, + // even though that is not a great proxy. + byteSize += stack.serialize().toString().getBytes(StandardCharsets.UTF_8).length; + + return byteSize; + } + + private long getInventorySize(ItemStack[] contents) { + long size = 0L; + for (ItemStack stack : contents) { + size += getItemSize(stack); + } + return size; } - private void stripPages(ItemStack book) { - BookMeta bookMeta = (BookMeta) book.getItemMeta(); + // Prevent players from creating big books + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onBookEdit(PlayerEditBookEvent event) { + final String serializedPages = String.join("", event.getNewBookMeta().getPages()); + if ( + serializedPages.getBytes(StandardCharsets.UTF_8).length > maxBookSize + || serializedPages.getBytes(StandardCharsets.UTF_16).length > maxBookSize + ) { + event.setCancelled(true); + } + } - List pages = new ArrayList<>(bookMeta.getPageCount()); + // Attempt to stop players from picking up large items + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPlayerAttemptPickupItem(PlayerAttemptPickupItemEvent event) { + final long itemSize = getItemSize(event.getItem().getItemStack()); + if (itemSize > maxItemSize) { + event.setCancelled(true); + return; + } - for (String page : bookMeta.getPages()) { - if (page.getBytes(StandardCharsets.UTF_8).length <= maxByteSizePerPage) - pages.add(page); + // Check if the total inventory size would be larger than the maximum inventory size + if (itemSize + getInventorySize(event.getPlayer().getInventory().getContents()) > maxInventorySize) { + event.setCancelled(true); } + } - bookMeta.setPages(pages); - book.setItemMeta(bookMeta); + private void sanitizeInventory(PlayerInventory inventory) { + Iterator invIterator = inventory.iterator(); + while (invIterator.hasNext()) { + if (getItemSize(invIterator.next()) > maxItemSize) { + invIterator.remove(); + } + } } - @EventHandler(priority = EventPriority.NORMAL) + // Attempt to clear off big items from affected players + @EventHandler(priority = EventPriority.LOW) private void onJoin(PlayerJoinEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.LOW) private void onLeave(PlayerQuitEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } - @EventHandler(priority = EventPriority.NORMAL) + @EventHandler(priority = EventPriority.LOW) private void onKick(PlayerKickEvent event) { - clearBooks(event.getPlayer()); + sanitizeInventory(event.getPlayer().getInventory()); } -} +} \ No newline at end of file