From 541dc1238a446137a53506e945c31c87321605b2 Mon Sep 17 00:00:00 2001 From: NotSoDelayed <72163224+NotSoDelayed@users.noreply.github.com> Date: Sat, 23 Nov 2024 14:08:36 +0800 Subject: [PATCH] Changeable Max Stack Size for Items and Inventory (#6698) * Changeable max stack size for item and inventory * Changeable max stack size for item and inventory * Fix required plugins versioning * Fix human error and removing redundant debug message * Fix breaking change to inventory max stack size in 1.20.5 * Requested changes * Annotation correction * More tests * Check for ItemType#getRandom() nullability * Check for ItemType#getRandom() nullability * Remove license header (#6684) * Java 17 and some code optimisations * Requested changes * Improve examples * Requested changes * Minor logic change for changer * chezburger simplified --------- Co-authored-by: Moderocky Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/expressions/ExprMaxStack.java | 128 +++++++++++++----- .../syntaxes/expressions/ExprMaxStack.sk | 62 ++++++++- 2 files changed, 152 insertions(+), 38 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprMaxStack.java b/src/main/java/ch/njol/skript/expressions/ExprMaxStack.java index 77b65fa5191..56744bcddff 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprMaxStack.java +++ b/src/main/java/ch/njol/skript/expressions/ExprMaxStack.java @@ -1,57 +1,102 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ package ch.njol.skript.expressions; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.event.Event; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; + +import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; -/** - * @author joeuguce99 - */ @Name("Maximum Stack Size") -@Description("The maximum stack size of the specified material, e.g. 64 for torches, 16 for buckets, and 1 for swords.") -@Examples("send \"You can only pick up %max stack size of player's tool% of %type of (player's tool)%\" to player") -@Since("2.1") -public class ExprMaxStack extends SimplePropertyExpression { +@Description({ + "The maximum stack size of an item (e.g. 64 for torches, 16 for buckets, 1 for swords, etc.) or inventory.", + "In 1.20.5+, the maximum stack size of items can be changed to any integer from 1 to 99, and stacked up to the maximum stack size of the inventory they're in." +}) +@Examples({ + "send \"You can hold %max stack size of player's tool% of %type of player's tool% in a slot.\" to player", + "set the maximum stack size of inventory of all players to 16", + "add 8 to the maximum stack size of player's tool", + "reset the maximum stack size of {_gui}" +}) +@Since("2.1, INSERT VERSION (changeable, inventories)") +@RequiredPlugins("Spigot 1.20.5+ (changeable)") +public class ExprMaxStack extends SimplePropertyExpression { static { - register(ExprMaxStack.class, Long.class, "max[imum] stack[[ ]size]", "itemtype"); + register(ExprMaxStack.class, Integer.class, "max[imum] stack[[ ]size]", "itemtypes/inventories"); } - - @SuppressWarnings("null") + + private static final boolean CHANGEABLE_ITEM_STACK_SIZE = Skript.methodExists(ItemMeta.class, "setMaxStackSize", Integer.class); + @Override - public Long convert(ItemType itemType) { - Object random = itemType.getRandomStackOrMaterial(); - if (random instanceof Material) - return (long) ((Material) random).getMaxStackSize(); - return (long) ((ItemStack) random).getMaxStackSize(); + public @Nullable Integer convert(Object from) { + if (from instanceof ItemType itemType) + return getMaxStackSize(itemType); + if (from instanceof Inventory inventory) + return inventory.getMaxStackSize(); + return null; } @Override - public Class getReturnType() { - return Long.class; + public @Nullable Class[] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, RESET, SET -> { + if (!CHANGEABLE_ITEM_STACK_SIZE && ItemType.class.isAssignableFrom(getExpr().getReturnType())) { + Skript.error("Changing the maximum stack size of items requires Minecraft 1.20.5 or newer!"); + yield null; + } + yield CollectionUtils.array(Integer.class); + } + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + int change = delta == null ? 0 : ((Number) delta[0]).intValue(); + for (Object source : getExpr().getArray(event)) { + if (source instanceof ItemType itemType) { + if (!CHANGEABLE_ITEM_STACK_SIZE) + continue; + int size = getMaxStackSize(itemType); + switch (mode) { + case ADD -> size += change; + case SET -> size = change; + case REMOVE -> size -= change; + } + ItemMeta meta = itemType.getItemMeta(); + // Minecraft only accepts stack size from 1 to 99 + meta.setMaxStackSize(mode != ChangeMode.RESET ? Math2.fit(1, size, 99) : null); + itemType.setItemMeta(meta); + } else if (source instanceof Inventory inventory) { + int size = inventory.getMaxStackSize(); + switch (mode) { + case ADD -> size += change; + case SET -> size = change; + case REMOVE -> size -= change; + case RESET -> size = Bukkit.createInventory(null, inventory.getType()).getMaxStackSize(); + } + inventory.setMaxStackSize(size); + } + } + } + + @Override + public Class getReturnType() { + return Integer.class; } @Override @@ -59,4 +104,13 @@ protected String getPropertyName() { return "maximum stack size"; } + private static int getMaxStackSize(ItemType itemType) { + Object item = itemType.getRandomStackOrMaterial(); + if (item instanceof ItemStack stack) + return stack.getMaxStackSize(); + if (item instanceof Material material) + return material.getMaxStackSize(); + throw new UnsupportedOperationException(); + } + } diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMaxStack.sk b/src/test/skript/tests/syntaxes/expressions/ExprMaxStack.sk index 73694e0d5c2..1ec4235a2f9 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprMaxStack.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprMaxStack.sk @@ -1,4 +1,64 @@ -test "max stack": +test "max stack - itemtype": assert max stack size of diamond sword is 1 with "diamond sword max stack size failed" assert max stack size of bucket is 16 with "bucket max stack size failed" assert max stack size of dirt is 64 with "dirt max stack size failed" + + # edge case + assert max stack size of {_null} is not set with "max stack size of non itemtype expected to fail ##1" + assert max stack size of {_null} is 0 to fail with "max stack size of non itemtype expected to fail ##2" + assert max stack size of diamond and diamond sword is 64 and 1 with "max stack size of itemtypes 'and' case failed" + assert max stack size of diamond or diamond sword is 64 or 1 with "max stack size of itemtypes 'or' case failed" + loop any log and any wool: + assert max stack size of loop-value is 64 with "max stack size of category itemtypes (%loop-value%) failed" + +test "max stack override - itemtype" when running minecraft "1.20.5": + set {_item} to diamond + set max stack size of {_item} to 32 + assert max stack size of {_item} is 32 with "diamond should have max stack size of 32" + add 2 to max stack size of {_item} + assert max stack size of {_item} is 34 with "diamond should have max stack size of 34" + remove 4 from max stack size of {_item} + assert max stack size of {_item} is 30 with "diamond should have max stack size of 30" + reset max stack size of {_item} + assert max stack size of {_item} is 64 with "diamond should have max stack size of 64" + + set {_item} to bucket + set max stack size of {_item} to 24 + assert max stack size of {_item} is 24 with "bucket should have max stack size of 24" + add 2 to max stack size of {_item} + assert max stack size of {_item} is 26 with "bucket should have max stack size of 26" + remove 4 from max stack size of {_item} + assert max stack size of {_item} is 22 with "bucket should have max stack size of 22" + reset max stack size of {_item} + assert max stack size of {_item} is 16 with "bucket should have max stack size of 16" + + set {_item} to diamond sword + set max stack size of {_item} to 16 + assert max stack size of {_item} is 16 with "diamond sword should have max stack size of 16" + add 2 to max stack size of {_item} + assert max stack size of {_item} is 18 with "diamond sword should have max stack size of 18" + remove 4 from max stack size of {_item} + assert max stack size of {_item} is 14 with "diamond sword should have max stack size of 14" + reset max stack size of {_item} + assert max stack size of {_item} is 1 with "diamond sword should have max stack size of 1" + + # edge case + loop any log and any wool: + set {_edge} to loop-value + set max stack size of {_edge} to 1 + assert max stack size of {_edge} is 1 with "max stack size override of category itemtypes (%{_edge}%) failed" + +test "max stack override - inventory": + set {_default} to 99 # 1.20.5 and newer + if running below minecraft "1.20.5": + set {_default} to 64 + set {_inv} to a new chest inventory with 1 rows + assert max stack size of {_inv} is {_default} with "inventory should have max stack size of %{_default}%" + set max stack size of {_inv} to 32 + assert max stack size of {_inv} is 32 with "inventory should have max stack size of 32" + add 2 to max stack size of {_inv} + assert max stack size of {_inv} is 34 with "inventory should have max stack size of 34" + remove 4 from max stack size of {_inv} + assert max stack size of {_inv} is 30 with "inventory should have max stack size of 30" + reset max stack size of {_inv} + assert max stack size of {_inv} is {_default} with "inventory should have factory max stack size of %{_default}%"