Refactor book byte enforcement by replacing BookByteChunkLimitListener with simplified BookByteLimitListener. Adjust byte limits and improve oversized book handling logic.
This commit is contained in:
parent
e03c51198c
commit
57b5b8fe84
|
|
@ -43,7 +43,7 @@ public final class PlayerUtils extends JavaPlugin {
|
|||
pluginManager.registerEvents(new BlockBlockUseEvent(), this);
|
||||
pluginManager.registerEvents(new PlayerJoin(this), this);
|
||||
pluginManager.registerEvents(new BookWriteEvent(), this);
|
||||
pluginManager.registerEvents(new BookByteChunkLimitListener(this), this);
|
||||
pluginManager.registerEvents(new BookByteLimitListener(), this);
|
||||
|
||||
RotateBlockEvent rotateBlockEvent = new RotateBlockEvent();
|
||||
pluginManager.registerEvents(rotateBlockEvent, this);
|
||||
|
|
|
|||
|
|
@ -1,285 +0,0 @@
|
|||
package com.alttd.playerutils.event_listeners;
|
||||
|
||||
import com.alttd.playerutils.PlayerUtils;
|
||||
import com.alttd.playerutils.util.BookByteUtils;
|
||||
import com.alttd.playerutils.util.ChunkBookByteTracker;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.ItemDespawnEvent;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
import org.bukkit.event.inventory.*;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
public class BookByteChunkLimitListener implements Listener {
|
||||
|
||||
private final PlayerUtils plugin;
|
||||
private static final int CHUNK_CAP = BookByteUtils.MAX_BOOK_BYTES * 3; // 65k * 3
|
||||
|
||||
public BookByteChunkLimitListener(PlayerUtils plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
// World item drops -------------------------------------------------
|
||||
@EventHandler
|
||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||
Item item = event.getItemDrop();
|
||||
ItemStack stack = item.getItemStack();
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
return;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
Chunk chunk = item.getLocation().getChunk();
|
||||
if (!ChunkBookByteTracker.tryAddBytes(chunk, bytes, CHUNK_CAP, plugin)) {
|
||||
log.warn("Player {} [{}] tried to drop a book of {} bytes to a chunk that is already saturated", event.getPlayer().getName(), event.getPlayer().getUniqueId(), bytes);
|
||||
event.setCancelled(true);
|
||||
Player p = event.getPlayer();
|
||||
p.sendRichMessage("<red>You can't drop this here.</red>");
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPickup(EntityPickupItemEvent event) {
|
||||
Item item = event.getItem();
|
||||
ItemStack stack = event.getItem().getItemStack();
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
return;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
ChunkBookByteTracker.removeBytes(item.getLocation().getChunk(), bytes, plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onItemDespawn(ItemDespawnEvent event) {
|
||||
Item item = event.getEntity();
|
||||
ItemStack stack = item.getItemStack();
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
return;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
ChunkBookByteTracker.removeBytes(item.getLocation().getChunk(), bytes, plugin);
|
||||
}
|
||||
|
||||
// Player death drops -------------------------------------------------
|
||||
@EventHandler
|
||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||
if (event.getDrops().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Chunk chunk = event.getEntity().getLocation().getChunk();
|
||||
int current = ChunkBookByteTracker.getBytes(chunk, plugin);
|
||||
int available = CHUNK_CAP - current;
|
||||
if (available <= 0) {
|
||||
// remove all large books from drops
|
||||
event.getDrops().removeIf(stack -> {
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
return false;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
boolean isByteLimitExceeded = bytes > 0;
|
||||
if (isByteLimitExceeded) {
|
||||
log.warn("Player {} [{}] tried to drop a book of {} bytes by dying in a chunk that is already saturated", event.getEntity().getName(), event.getEntity().getUniqueId(), bytes);
|
||||
}
|
||||
return isByteLimitExceeded; // despawn
|
||||
});
|
||||
return;
|
||||
}
|
||||
int usedFromDeath = 0;
|
||||
for (int i = 0; i < event.getDrops().size(); i++) {
|
||||
ItemStack stack = event.getDrops().get(i);
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
continue;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
if (bytes <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (usedFromDeath + bytes > available) {
|
||||
// remove excess
|
||||
log.warn("Player {} [{}] tried to drop a book of {} bytes by dying in a chunk that would be saturated", event.getEntity().getName(), event.getEntity().getUniqueId(), bytes);
|
||||
event.getDrops().set(i, null);
|
||||
} else {
|
||||
usedFromDeath += bytes;
|
||||
}
|
||||
}
|
||||
event.getDrops().removeIf(Objects::isNull);
|
||||
if (usedFromDeath > 0) {
|
||||
ChunkBookByteTracker.tryAddBytes(chunk, usedFromDeath, CHUNK_CAP, plugin); // will fit by construction
|
||||
}
|
||||
}
|
||||
|
||||
// Container interactions --------------------------------------------
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
InventoryView view = event.getView();
|
||||
Inventory top = view.getTopInventory();
|
||||
boolean topIsContainer = event.getRawSlot() < top.getSize();
|
||||
|
||||
// Shift-click from bottom to top (adding to container)
|
||||
if (!topIsContainer) {
|
||||
if (!event.isShiftClick()) {
|
||||
return;
|
||||
}
|
||||
ItemStack current = event.getCurrentItem();
|
||||
if (current == null) {
|
||||
return;
|
||||
}
|
||||
if (!BookByteUtils.isWrittenBook(current)) {
|
||||
return;
|
||||
}
|
||||
int addBytes = BookByteUtils.computeBytes(current);
|
||||
if (addBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
Chunk chunk = getContainerChunk(top);
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
if (!ChunkBookByteTracker.tryAddBytes(chunk, addBytes, CHUNK_CAP, plugin)) {
|
||||
log.warn("Player {} [{}] tried to shift click a book of {} bytes into a container in a chunk that is already saturated", event.getWhoClicked().getName(), event.getWhoClicked().getUniqueId(), addBytes);
|
||||
event.setCancelled(true);
|
||||
sendCantStore(event.getWhoClicked());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Interactions within the top container inventory
|
||||
Chunk chunk = getContainerChunk(top);
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack current = event.getCurrentItem(); // item in the clicked slot (may be removed)
|
||||
ItemStack cursor = event.getCursor(); // item on cursor (may be added)
|
||||
|
||||
int removeBytes = (BookByteUtils.isWrittenBook(current)) ? BookByteUtils.computeBytes(current) : 0;
|
||||
int addBytes = !cursor.getType().isAir() && BookByteUtils.isWrittenBook(cursor) ? BookByteUtils.computeBytes(cursor) : 0;
|
||||
|
||||
// If shift-click from top to bottom: only removal occurs
|
||||
if (event.isShiftClick()) {
|
||||
if (removeBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
ChunkBookByteTracker.removeBytes(chunk, removeBytes, plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
// If nothing book-related, ignore
|
||||
if (removeBytes <= 0 && addBytes <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int currentTracked = ChunkBookByteTracker.getBytes(chunk, plugin);
|
||||
int projected = currentTracked - removeBytes + addBytes;
|
||||
if (projected > CHUNK_CAP) {
|
||||
log.warn("Player {} [{}] tried to add a book of {} bytes to a chunk that would be saturated", event.getWhoClicked().getName(), event.getWhoClicked().getUniqueId(), addBytes);
|
||||
event.setCancelled(true);
|
||||
sendCantStore(event.getWhoClicked());
|
||||
return;
|
||||
}
|
||||
// Apply adjustments
|
||||
ChunkBookByteTracker.setBytes(chunk, Math.max(0, projected), plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryDrag(InventoryDragEvent event) {
|
||||
// If any of the added slots are in the top inventory, block if needed
|
||||
InventoryView view = event.getView();
|
||||
Inventory top = view.getTopInventory();
|
||||
ItemStack cursor = event.getOldCursor();
|
||||
if (cursor.getType().isAir()) {
|
||||
return;
|
||||
}
|
||||
if (!BookByteUtils.isWrittenBook(cursor)) {
|
||||
return;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(cursor);
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
boolean affectsTop = event.getRawSlots().stream().anyMatch(slot -> slot < top.getSize());
|
||||
if (!affectsTop) {
|
||||
return;
|
||||
}
|
||||
Chunk chunk = getContainerChunk(top);
|
||||
if (chunk == null) {
|
||||
return;
|
||||
}
|
||||
if (!ChunkBookByteTracker.tryAddBytes(chunk, bytes, CHUNK_CAP, plugin)) {
|
||||
log.warn("Player {} [{}] tried to add a book of {} bytes to a chunk that is already saturated", event.getWhoClicked().getName(), event.getWhoClicked().getUniqueId(), bytes);
|
||||
event.setCancelled(true);
|
||||
sendCantStore(event.getWhoClicked());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
|
||||
// hopper or other automation
|
||||
ItemStack stack = event.getItem();
|
||||
if (!BookByteUtils.isWrittenBook(stack)) {
|
||||
return;
|
||||
}
|
||||
int bytes = BookByteUtils.computeBytes(stack);
|
||||
if (bytes <= 0) {
|
||||
return;
|
||||
}
|
||||
Inventory dest = event.getDestination();
|
||||
// Only enforce when destination is a block inventory in world
|
||||
Chunk destChunk = InventoryChunkResolver.getInventoryChunk(dest);
|
||||
if (destChunk == null) {
|
||||
return;
|
||||
}
|
||||
if (!ChunkBookByteTracker.tryAddBytes(destChunk, bytes, CHUNK_CAP, plugin)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
// will move; remove from source if it was tracked in a container within a chunk
|
||||
Chunk srcChunk = InventoryChunkResolver.getInventoryChunk(event.getSource());
|
||||
if (srcChunk != null) {
|
||||
ChunkBookByteTracker.removeBytes(srcChunk, bytes, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCantStore(HumanEntity who) {
|
||||
who.sendRichMessage("<red>You can't store this here.</red>");
|
||||
}
|
||||
|
||||
private Chunk getContainerChunk(Inventory inv) {
|
||||
return InventoryChunkResolver.getInventoryChunk(inv);
|
||||
}
|
||||
|
||||
// Helper to resolve container inventory chunks
|
||||
private static class InventoryChunkResolver {
|
||||
static Chunk getInventoryChunk(Inventory inv) {
|
||||
if (inv == null) {
|
||||
return null;
|
||||
}
|
||||
if (inv.getHolder() instanceof org.bukkit.block.BlockState state) {
|
||||
return state.getLocation().getChunk();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.alttd.playerutils.event_listeners;
|
||||
|
||||
import com.alttd.playerutils.util.BookByteUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.ItemSpawnEvent;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@Slf4j
|
||||
public class BookByteLimitListener implements Listener {
|
||||
|
||||
private boolean isOversizedBook(ItemStack stack) {
|
||||
boolean isOversizedBook = BookByteUtils.shouldCountForBookByteLimit(stack)
|
||||
&& BookByteUtils.computeBytes(stack) > BookByteUtils.MAX_BOOK_BYTES;
|
||||
if (isOversizedBook) {
|
||||
log.warn("Player tried to drop an oversized book");
|
||||
Component message = MiniMessage.miniMessage().deserialize(
|
||||
"<red>Player tried to drop an oversized book</red>");
|
||||
Bukkit.broadcast(message, "staffutils.patrol");
|
||||
}
|
||||
return isOversizedBook;
|
||||
}
|
||||
|
||||
private boolean isOversizedBook(ItemStack stack, HumanEntity humanEntity) {
|
||||
boolean isOversizedBook = BookByteUtils.shouldCountForBookByteLimit(stack)
|
||||
&& BookByteUtils.computeBytes(stack) > BookByteUtils.MAX_BOOK_BYTES;
|
||||
if (isOversizedBook) {
|
||||
log.warn("{} [{}] tried to drop an oversized book", humanEntity.getName(), humanEntity.getUniqueId());
|
||||
Component message = MiniMessage.miniMessage().deserialize(
|
||||
"<red>Player <player> tried to drop an oversized book</red>",
|
||||
Placeholder.unparsed("player", humanEntity.getName()));
|
||||
Bukkit.broadcast(message, "staffutils.patrol");
|
||||
}
|
||||
return isOversizedBook;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onItemSpawn(ItemSpawnEvent event) {
|
||||
Item item = event.getEntity();
|
||||
if (isOversizedBook(item.getItemStack())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDrop(PlayerDropItemEvent event) {
|
||||
if (isOversizedBook(event.getItemDrop().getItemStack(), event.getPlayer())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
InventoryAction action = event.getAction();
|
||||
|
||||
switch (action) {
|
||||
case PLACE_ALL, PLACE_ONE, PLACE_SOME, SWAP_WITH_CURSOR -> {
|
||||
if (isOversizedBook(event.getCursor(), event.getWhoClicked())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
case MOVE_TO_OTHER_INVENTORY -> {
|
||||
if (isOversizedBook(event.getCurrentItem(), event.getWhoClicked())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
case HOTBAR_SWAP -> {
|
||||
ItemStack hotbarItem = event.getWhoClicked().getInventory().getItem(event.getHotbarButton());
|
||||
if (isOversizedBook(hotbarItem, event.getWhoClicked())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryDrag(InventoryDragEvent event) {
|
||||
if (isOversizedBook(event.getOldCursor(), event.getWhoClicked())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryMoveItem(InventoryMoveItemEvent event) {
|
||||
if (isOversizedBook(event.getItem())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,10 @@ package com.alttd.playerutils.event_listeners;
|
|||
|
||||
import com.alttd.playerutils.util.BookByteUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
|
@ -11,25 +15,29 @@ import org.bukkit.inventory.meta.BookMeta;
|
|||
@Slf4j
|
||||
public class BookWriteEvent implements Listener {
|
||||
|
||||
private static final int WARN_BOOK_BYTES = 10_000;
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerEditBook(PlayerEditBookEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (player.hasPermission("playerutils.book-write.bypass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
BookMeta meta = event.getNewBookMeta();
|
||||
|
||||
int totalBytes = BookByteUtils.computeBytes(meta);
|
||||
|
||||
if (totalBytes > BookByteUtils.MAX_BOOK_BYTES) {
|
||||
log.warn("Player {} [{}] tried to write a book with {} bytes", player.getName(), player.getUniqueId(), totalBytes);
|
||||
log.warn("Player {} [{}] tried to write a book with {} bytes",
|
||||
player.getName(), player.getUniqueId(), totalBytes);
|
||||
event.setCancelled(true);
|
||||
} else if (totalBytes > WARN_BOOK_BYTES) {
|
||||
log.warn("Player {} [{}] wrote a book with {} bytes", player.getName(), player.getUniqueId(), totalBytes);
|
||||
Component message = MiniMessage.miniMessage().deserialize(
|
||||
"<red>Player <player> tried to write a book with <bytes> bytes</red>",
|
||||
Placeholder.unparsed("player", player.getName()), Placeholder.parsed("bytes", String.valueOf(totalBytes)));
|
||||
Bukkit.broadcast(message, "staffutils.patrol");
|
||||
} else if (totalBytes > BookByteUtils.BIG_BOOK_BYTES) {
|
||||
log.warn("Player {} [{}] wrote a large book with {} bytes",
|
||||
player.getName(), player.getUniqueId(), totalBytes);
|
||||
Component message = MiniMessage.miniMessage().deserialize(
|
||||
"<red>Player <player> wrote a book with <bytes> bytes</red>",
|
||||
Placeholder.unparsed("player", player.getName()), Placeholder.parsed("bytes", String.valueOf(totalBytes)));
|
||||
Bukkit.broadcast(message, "staffutils.patrol");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ package com.alttd.playerutils.util;
|
|||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
|
|
@ -16,16 +19,36 @@ public final class BookByteUtils {
|
|||
}
|
||||
|
||||
// 65,000 bytes per book (below CoreProtect hard limit ~65,535)
|
||||
public static final int MAX_BOOK_BYTES = 65_000;
|
||||
public static final int MAX_BOOK_BYTES = 30_000;
|
||||
public static final int BIG_BOOK_BYTES = 10_000;
|
||||
|
||||
public static boolean isWrittenBook(ItemStack stack) {
|
||||
public static boolean shouldCountForBookByteLimit(ItemStack stack) {
|
||||
if (stack == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(stack.getItemMeta() instanceof BookMeta meta)) {
|
||||
Material type = stack.getType();
|
||||
if (type == Material.WRITTEN_BOOK || type == Material.WRITABLE_BOOK) {
|
||||
return true;
|
||||
}
|
||||
if (stack.getItemMeta() instanceof BookMeta) {
|
||||
return true;
|
||||
}
|
||||
if (!(stack.getItemMeta() instanceof BlockStateMeta bsm) || !(bsm.getBlockState() instanceof ShulkerBox shulker)) {
|
||||
return false;
|
||||
}
|
||||
return meta.hasAuthor() || meta.hasPages() || meta.hasTitle();
|
||||
for (ItemStack content : shulker.getInventory().getContents()) {
|
||||
if (content == null) {
|
||||
continue;
|
||||
}
|
||||
Material contentType = content.getType();
|
||||
if (contentType == Material.WRITTEN_BOOK || contentType == Material.WRITABLE_BOOK) {
|
||||
return true;
|
||||
}
|
||||
if (content.getItemMeta() instanceof BookMeta) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,11 +83,24 @@ public final class BookByteUtils {
|
|||
if (stack == null) {
|
||||
return 0;
|
||||
}
|
||||
if (!(stack.getItemMeta() instanceof BookMeta meta)) {
|
||||
return 0;
|
||||
// Direct written book
|
||||
if (stack.getItemMeta() instanceof BookMeta meta) {
|
||||
int perBook = computeBytes(meta);
|
||||
return perBook * Math.max(1, stack.getAmount());
|
||||
}
|
||||
// written books do not stack; still, be safe and multiply by amount if it ever changes
|
||||
int perBook = computeBytes(meta);
|
||||
return perBook * Math.max(1, stack.getAmount());
|
||||
// Shulker box: sum bytes of contained written books
|
||||
if (stack.getItemMeta() instanceof BlockStateMeta bsm && bsm.getBlockState() instanceof ShulkerBox shulker) {
|
||||
int total = 0;
|
||||
for (ItemStack content : shulker.getInventory().getContents()) {
|
||||
if (content == null) {
|
||||
continue;
|
||||
}
|
||||
if (content.getItemMeta() instanceof BookMeta bookMeta) {
|
||||
total += computeBytes(bookMeta) * Math.max(1, content.getAmount());
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package com.alttd.playerutils.util;
|
||||
|
||||
import com.alttd.playerutils.PlayerUtils;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
public final class ChunkBookByteTracker {
|
||||
|
||||
private static NamespacedKey BYTES_KEY(PlayerUtils plugin) {
|
||||
return NamespacedKey.fromString("big_book_bytes", plugin);
|
||||
}
|
||||
|
||||
private ChunkBookByteTracker() {}
|
||||
|
||||
public static int getBytes(Chunk chunk, PlayerUtils plugin) {
|
||||
PersistentDataContainer pdc = chunk.getPersistentDataContainer();
|
||||
Integer bytes = pdc.get(BYTES_KEY(plugin), PersistentDataType.INTEGER);
|
||||
return bytes == null ? 0 : bytes;
|
||||
}
|
||||
|
||||
public static void setBytes(Chunk chunk, int bytes, PlayerUtils plugin) {
|
||||
PersistentDataContainer pdc = chunk.getPersistentDataContainer();
|
||||
pdc.set(BYTES_KEY(plugin), PersistentDataType.INTEGER, Math.max(0, bytes));
|
||||
}
|
||||
|
||||
public static boolean tryAddBytes(Chunk chunk, int add, int cap, PlayerUtils plugin) {
|
||||
int current = getBytes(chunk, plugin);
|
||||
if ((long) current + add > cap) {
|
||||
return false;
|
||||
}
|
||||
setBytes(chunk, current + add, plugin);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void removeBytes(Chunk chunk, int remove, PlayerUtils plugin) {
|
||||
int current = getBytes(chunk, plugin);
|
||||
int newVal = current - remove;
|
||||
if (newVal < 0) newVal = 0;
|
||||
setBytes(chunk, newVal, plugin);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user