Skip to content

Commit

Permalink
[Bug fix] See issues [pop4959#238], [pop4959#239] for details.
Browse files Browse the repository at this point in the history
- magnet flag can turn on, on non-container block.
- magnet feature behavior is incorrect with mult container.
- magnet feature behavior is incorrect with furnace-like.

- droptransfer feature can bind with non-container block.
- droptransfer feature cleared item when bind a non-container block.
- droptransfer feature can nested shulker box.
  • Loading branch information
thefatwhale committed Sep 6, 2024
1 parent 84b0e90 commit c91c0ca
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 66 deletions.
228 changes: 185 additions & 43 deletions src/main/java/com/griefcraft/lwc/LWC.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Furnace;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.type.Chest;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.FurnaceInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
Expand Down Expand Up @@ -442,65 +445,204 @@ public boolean canAdminProtection(Player player, Protection protection) {
return event.getAccess() == Permission.Access.ADMIN;
}

/**
* Calculate The number of items that can be deposit in a furnace-like.
*
* Fuel slot allows only fuel items.
* Result slot does not allow deposits.
*
* @param furnaceLike (Not Null) Furnace-like block.
* @param itemStack (Not Null) Item stack for deposit.
* @return Number of items that can be deposit.
*/
public int getMaxDepositAmount(Furnace furnaceLike, ItemStack itemStack) {
FurnaceInventory inventory = furnaceLike.getInventory();
ItemStack sourceSlot = inventory.getSmelting();
ItemStack fuelSlot = inventory.getFuel();

// source slot is empty, can deposit
if (sourceSlot == null) {
return itemStack.getAmount();
}

boolean isItemStackFuel = itemStack.getType().isFuel();

// source slot not empty, but can stack deposit
if (sourceSlot.getAmount() < sourceSlot.getMaxStackSize()) {
if (itemStack.isSimilar(sourceSlot)) {
return Math.min(
sourceSlot.getMaxStackSize() - sourceSlot.getAmount(),
itemStack.getAmount()
);
}
}

// can't stack deposit to source slot or source slot is full
// mean the same thing
if (fuelSlot == null) {
if (isItemStackFuel) {
return itemStack.getAmount();
}
return 0;
}
if (fuelSlot.getAmount() < fuelSlot.getMaxStackSize()) {
if (itemStack.isSimilar(fuelSlot)) {
return Math.min(
fuelSlot.getMaxStackSize() - fuelSlot.getAmount(),
itemStack.getAmount()
);
}
}

return 0;
}

/**
* Calculate The number of items that can be deposit in a container.
*
* @param block (Not Null) Container block has inventory.
* @param itemStack (Not Null) Item stack for container deposit.
* @return Number of items that can be deposit.
*/
public int getMaxDepositAmount(Block block, ItemStack itemStack) {
BlockState blockState = block.getState();

// this block has neither block status nor inventory
if (blockState == null || !(blockState instanceof InventoryHolder)) {
return 0;
}

// nesting is forbidden on shulker boxes
if (blockState instanceof ShulkerBox && itemStack.getType().toString().endsWith("SHULKER_BOX")) {
return 0;
}

Inventory inventory = ((InventoryHolder) blockState).getInventory();

// for Furnace-like blocks
if (blockState instanceof Furnace) {
return this.getMaxDepositAmount((Furnace) blockState, itemStack);
}

// has empty slot, can deposit?
if (inventory.firstEmpty() != -1) {
return itemStack.getAmount();
}

// has no empty slot, but can stack deposit?
if (itemStack.getMaxStackSize() == 1) {
// non-stackable items
return 0;
}
Map<Integer, ? extends ItemStack> sameTypeItemStacks = inventory.all(itemStack.getType());
for (ItemStack sameItemStack : sameTypeItemStacks.values()) {
if (sameItemStack.getAmount() >= sameItemStack.getMaxStackSize()) {
continue;
}
if (itemStack.isSimilar(sameItemStack)) {
return Math.min(
sameItemStack.getMaxStackSize() - sameItemStack.getAmount(),
itemStack.getAmount()
);
}
}

return 0;
}

/**
* Deposit items into an inventory chest Works with double chests.
*
*
* NOTE: Returns empty map if the entire item is deposit,
* non-empty map if a partial amount of items is deposit,
* null if item can't be deposit.
*
* @param block
* @param itemStack
* @return remaining items (if any)
* @return (Nullable) remaining items.
*/
public Map<Integer, ItemStack> depositItems(Block block, ItemStack itemStack) {
BlockState blockState;
BlockState blockState = block.getState();
// not a container, leave the item intact
if (blockState == null || !(blockState instanceof InventoryHolder)) {
return null;
}

if ((blockState = block.getState()) != null && (blockState instanceof InventoryHolder)) {
Block doubleChestBlock = null;
InventoryHolder holder = (InventoryHolder) blockState;
Block doubleChestBlock = null;
if (DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) {
doubleChestBlock = findAdjacentDoubleChest(block);
}

if (DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) {
doubleChestBlock = findAdjacentDoubleChest(block);
} else if (block.getType() == Material.FURNACE) {
Inventory inventory = holder.getInventory();
Map<Integer, ItemStack> empty = new HashMap<>();
int itemStackAmount = itemStack.getAmount();

if (inventory.getItem(0) != null && inventory.getItem(1) != null) {
if (inventory.getItem(0).getType() == itemStack.getType()
&& inventory.getItem(0)
.getMaxStackSize() >= (inventory.getItem(0).getAmount() + itemStack.getAmount())) {
// ItemStack fits on Slot 0
} else if (inventory.getItem(1).getType() == itemStack.getType()
&& inventory.getItem(1)
.getMaxStackSize() >= (inventory.getItem(1).getAmount() + itemStack.getAmount())) {
// ItemStack fits on Slot 1
} else {
return null;
}
}
}
// remove unreasonable item
if (itemStackAmount <= 0) {
return empty;
}

if (itemStack.getAmount() <= 0) {
return new HashMap<Integer, ItemStack>();
}
// reached deposit limit, leave the item intact
int canDepositAmount = getMaxDepositAmount(block, itemStack);
if (canDepositAmount == 0) {
return null;
}

Map<Integer, ItemStack> remaining = holder.getInventory().addItem(itemStack);
Inventory inventory = ((InventoryHolder) blockState).getInventory();
Map<Integer, ItemStack> remaining;

// furnace-like is special, we don't want to handle it to addItem method directly
if (blockState instanceof Furnace) {
// for furnace-like blocks
Furnace furnaceLike = (Furnace) blockState;
FurnaceInventory snapshotInventory = furnaceLike.getSnapshotInventory();

ItemStack resultSlotBackup = snapshotInventory.getResult();
// avoid addItem method stack deposit into result slot
snapshotInventory.setResult(null);

// can't deposit them all
if (canDepositAmount < itemStackAmount) {
// split item stack into 2 parts
int restAmount = itemStackAmount - canDepositAmount;
ItemStack canDepositPart = itemStack.clone();
ItemStack theRestPart = itemStack.clone();

canDepositPart.setAmount(canDepositAmount);
theRestPart.setAmount(restAmount);
// deposit can deposit part, drop the rest part into item
remaining = snapshotInventory.addItem(canDepositPart);
remaining.put(-1, theRestPart);
itemStack.setAmount(restAmount);
} else {
remaining = snapshotInventory.addItem(itemStack);
}

// we have remainders, deal with it
// restore backup and commit update
snapshotInventory.setResult(resultSlotBackup);
furnaceLike.update(true, false);
} else {
// other container block
remaining = inventory.addItem(itemStack);
}

// we have remainders, deal with it
if (remaining.size() > 0) {
int key = remaining.keySet().iterator().next();
ItemStack remainingItemStack = remaining.get(key);

// is it a double chest ?????
if (doubleChestBlock != null) {
InventoryHolder holder2 = (InventoryHolder) doubleChestBlock.getState();
remaining = holder2.getInventory().addItem(remainingItemStack);
}

// recheck remaining in the event of double chest being used
if (remaining.size() > 0) {
int key = remaining.keySet().iterator().next();
ItemStack remainingItemStack = remaining.get(key);

// is it a double chest ?????
if (doubleChestBlock != null) {
InventoryHolder holder2 = (InventoryHolder) doubleChestBlock.getState();
remaining = holder2.getInventory().addItem(remainingItemStack);
}

// recheck remaining in the event of double chest being used
if (remaining.size() > 0) {
return remaining;
}
return remaining;
}
}

return new HashMap<Integer, ItemStack>();
return empty;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/griefcraft/modules/flag/BaseFlagModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import com.griefcraft.scripting.event.LWCProtectionInteractEvent;
import com.griefcraft.util.Colors;
import com.griefcraft.util.StringUtil;

import org.bukkit.block.BlockState;
import org.bukkit.block.Container;
import org.bukkit.command.CommandSender;

public class BaseFlagModule extends JavaModule {
Expand Down Expand Up @@ -85,6 +88,16 @@ public void onProtectionInteract(LWCProtectionInteractEvent event) {
flag = new Flag(type);
}

// magnet flag can't use on non-container block.
if (flag.getType() == Flag.Type.MAGNET) {
BlockState targetBlockState = protection.getBlock().getState();
if (!(targetBlockState instanceof Container)) {
lwc.sendLocale(player, "protection.interact.flag.magnet.notcontainer", "flag", StringUtil.capitalizeFirstLetter(flagName));
lwc.removeModes(player);
return;
}
}

if (shouldAdd) {
protection.addFlag(flag);
lwc.sendLocale(player, "protection.interact.flag.add", "flag", StringUtil.capitalizeFirstLetter(flagName));
Expand Down
50 changes: 29 additions & 21 deletions src/main/java/com/griefcraft/modules/flag/MagnetModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Container;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item;
Expand Down Expand Up @@ -154,27 +155,34 @@ public void run() {
List<Protection> protections = lwc.getPhysicalDatabase().loadProtections(world.getName(), x, y,
z, radius);
for (Protection protection : protections) {
if (protection.hasFlag(Flag.Type.MAGNET)) {

if (protection.getBukkitWorld().getName() != item.getWorld().getName())
continue;

// we only want inventory blocks
if (!(protection.getBlock().getState() instanceof InventoryHolder)) {
continue;
}

// never allow a shulker box to enter another shulker box
if (item.getItemStack().getType().toString().contains("SHULKER_BOX") && protection.getBlock().getType().toString().contains("SHULKER_BOX")) {
continue;
}

MagnetNode node = new MagnetNode();
node.item = item;
node.protection = protection;
items.offer(node);
break;
if (!protection.hasFlag(Flag.Type.MAGNET)) {
continue;
}

if (protection.getBukkitWorld().getName() != item.getWorld().getName())
continue;

// we only want container blocks
if (!(protection.getBlock().getState() instanceof Container)) {
continue;
}

// never allow a shulker box to enter another shulker box
if (item.getItemStack().getType().toString().contains("SHULKER_BOX") && protection.getBlock().getType().toString().contains("SHULKER_BOX")) {
continue;
}

// don't try to enter a full container
boolean isFull = lwc.getMaxDepositAmount(protection.getBlock(), stack) == 0;
if (isFull) {
continue;
}

MagnetNode node = new MagnetNode();
node.item = item;
node.protection = protection;
items.offer(node);
break;
}
}
}
Expand Down Expand Up @@ -206,7 +214,7 @@ public void run() {
return;
}

// we cancelled the item drop for some reason
// reached deposit limit, leave the item intact
if (remaining == null) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.griefcraft.scripting.event.LWCProtectionInteractEvent;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Container;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Item;
Expand Down Expand Up @@ -161,11 +162,12 @@ public void onProtectionInteract(LWCProtectionInteractEvent event) {
if (!canAccess) {
lwc.sendLocale(player, "protection.interact.dropxfer.noaccess");
} else {
if (event.getEvent().getClickedBlock() instanceof Container) {
Block clickedBlock = event.getEvent().getClickedBlock();
BlockState blockState = clickedBlock.getState();
if (!(blockState instanceof Container)) {
lwc.sendLocale(player, "protection.interact.dropxfer.notchest");
player.removeAllActions();
event.setResult(Result.CANCEL);

return;
}

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/lang/lwc_cn.properties
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ protection.onplace.create.finalize=%dark_green%成功创建了%type% %block%的
# Flag
protection.interact.flag.add=%dark_green%成功打开%dark_aqua% %flag%%dark_green%保护
protection.interact.flag.remove=%dark_red%成功移除%dark_aqua% %flag%%dark_red%保护
protection.interact.flag.magnet.notcontainer=%dark_aqua%%flag% %dark_red%功能只能应用在容器上

# Creation
protection.interact.create.password=%dark_aqua%为了方便起见,你在下次登陆前都不用输入密码了。
Expand Down
Loading

0 comments on commit c91c0ca

Please sign in to comment.