From cb292aeffe082c6fdfc345ca6e042913c871d37a Mon Sep 17 00:00:00 2001 From: Jeroen Date: Mon, 24 Jul 2023 23:25:25 +0200 Subject: [PATCH 01/66] skip already substituted parts when formatting message --- .../io/github/thebusybiscuit/slimefun4/core/debug/Debug.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java index 433ec5e1cd..cf7687dc9f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java @@ -106,7 +106,7 @@ public static void log(@Nonnull String test, @Nonnull String msg, @Nonnull Objec while ((i = msg.indexOf('{', i)) != -1 && msg.charAt(i + 1) == '}') { // Substring up to the opening brace `{`, add the variable for this and add the rest of the message msg = msg.substring(0, i) + vars[idx] + msg.substring(i + 2); - idx++; + i += ("" + vars[idx++]).length(); } return msg; From f60896f620e3d367df845bdebf8e1ef68d383746 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sun, 20 Aug 2023 02:37:24 +0200 Subject: [PATCH 02/66] fix geo miner voiding resources --- .../core/machines/MachineOperation.java | 6 +++ .../core/machines/MachineProcessor.java | 37 +++++++-------- .../implementation/items/geo/GEOMiner.java | 46 +++++++++---------- .../operations/MiningOperation.java | 29 +++++++++--- 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java index 6ee741882a..67fda66d29 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java @@ -57,4 +57,10 @@ default boolean isFinished() { return getRemainingTicks() <= 0; } + /** + * This method is called when a {@link MachineOperation} is interrupted before finishing. + * Implement to specify behaviour that should happen in this case. + */ + default void cancel() {} + } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index 33ccb4111d..4ccf41192c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -6,7 +6,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.apache.commons.lang.Validate; +import com.google.common.base.Preconditions; + import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; @@ -46,7 +47,7 @@ public class MachineProcessor { * The owner of this {@link MachineProcessor}. */ public MachineProcessor(@Nonnull MachineProcessHolder owner) { - Validate.notNull(owner, "The MachineProcessHolder cannot be null."); + Preconditions.checkArgument(owner != null, "The MachineProcessHolder cannot be null."); this.owner = owner; } @@ -93,8 +94,8 @@ public void setProgressBar(@Nullable ItemStack progressBar) { * {@link MachineOperation} has already been started at that {@link Location}. */ public boolean startOperation(@Nonnull Location loc, @Nonnull T operation) { - Validate.notNull(loc, "The location must not be null"); - Validate.notNull(operation, "The operation cannot be null"); + Preconditions.checkArgument(loc != null, "The location must not be null"); + Preconditions.checkArgument(operation != null, "The operation cannot be null"); return startOperation(new BlockPosition(loc), operation); } @@ -111,8 +112,8 @@ public boolean startOperation(@Nonnull Location loc, @Nonnull T operation) { * {@link MachineOperation} has already been started at that {@link Block}. */ public boolean startOperation(@Nonnull Block b, @Nonnull T operation) { - Validate.notNull(b, "The Block must not be null"); - Validate.notNull(operation, "The machine operation cannot be null"); + Preconditions.checkArgument(b != null, "The Block must not be null"); + Preconditions.checkArgument(operation != null, "The machine operation cannot be null"); return startOperation(new BlockPosition(b), operation); } @@ -129,8 +130,8 @@ public boolean startOperation(@Nonnull Block b, @Nonnull T operation) { * {@link MachineOperation} has already been started at that {@link BlockPosition}. */ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) { - Validate.notNull(pos, "The BlockPosition must not be null"); - Validate.notNull(operation, "The machine operation cannot be null"); + Preconditions.checkArgument(pos != null, "The BlockPosition must not be null"); + Preconditions.checkArgument(operation != null, "The machine operation cannot be null"); return machines.putIfAbsent(pos, operation) == null; } @@ -144,7 +145,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull Location loc) { - Validate.notNull(loc, "The location cannot be null"); + Preconditions.checkArgument(loc != null, "The location cannot be null"); return getOperation(new BlockPosition(loc)); } @@ -158,15 +159,13 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull Block b) { - Validate.notNull(b, "The Block cannot be null"); + Preconditions.checkArgument(b != null, "The Block cannot be null"); return getOperation(new BlockPosition(b)); } /** * This returns the current {@link MachineOperation} at that given {@link BlockPosition}. - * We don't need to validate our input here as that is already - * covered in our public methods. * * @param pos * The {@link BlockPosition} at which our machine is located. @@ -174,7 +173,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull BlockPosition pos) { - Validate.notNull(pos, "The BlockPosition must not be null"); + Preconditions.checkArgument(pos != null, "The BlockPosition must not be null"); return machines.get(pos); } @@ -189,7 +188,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull Location loc) { - Validate.notNull(loc, "The location should not be null"); + Preconditions.checkArgument(loc != null, "The location should not be null"); return endOperation(new BlockPosition(loc)); } @@ -204,7 +203,7 @@ public boolean endOperation(@Nonnull Location loc) { * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull Block b) { - Validate.notNull(b, "The Block should not be null"); + Preconditions.checkArgument(b != null, "The Block should not be null"); return endOperation(new BlockPosition(b)); } @@ -219,7 +218,7 @@ public boolean endOperation(@Nonnull Block b) { * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull BlockPosition pos) { - Validate.notNull(pos, "The BlockPosition cannot be null"); + Preconditions.checkArgument(pos != null, "The BlockPosition cannot be null"); T operation = machines.remove(pos); @@ -231,6 +230,8 @@ public boolean endOperation(@Nonnull BlockPosition pos) { if (operation.isFinished()) { Event event = new AsyncMachineOperationFinishEvent(pos, this, operation); Bukkit.getPluginManager().callEvent(event); + } else { + operation.cancel(); } return true; @@ -240,8 +241,8 @@ public boolean endOperation(@Nonnull BlockPosition pos) { } public void updateProgressBar(@Nonnull BlockMenu inv, int slot, @Nonnull T operation) { - Validate.notNull(inv, "The inventory must not be null."); - Validate.notNull(operation, "The MachineOperation must not be null."); + Preconditions.checkArgument(inv != null, "The inventory must not be null."); + Preconditions.checkArgument(operation != null, "The MachineOperation must not be null."); if (getProgressBar() == null) { // No progress bar, no need to update anything. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index e9b5d104ca..184215a2a8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -7,7 +7,8 @@ import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import org.apache.commons.lang.Validate; +import com.google.common.base.Preconditions; + import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -77,7 +78,7 @@ public GEOMiner(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeTy } @Override - public MachineProcessor getMachineProcessor() { + public @Nonnull MachineProcessor getMachineProcessor() { return processor; } @@ -121,8 +122,8 @@ public int getSpeed() { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final GEOMiner setCapacity(int capacity) { - Validate.isTrue(capacity > 0, "The capacity must be greater than zero!"); + public final @Nonnull GEOMiner setCapacity(int capacity) { + Preconditions.checkArgument(capacity > 0, "The capacity must be greater than zero!"); if (getState() == ItemState.UNREGISTERED) { this.energyCapacity = capacity; @@ -140,8 +141,8 @@ public final GEOMiner setCapacity(int capacity) { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final GEOMiner setProcessingSpeed(int speed) { - Validate.isTrue(speed > 0, "The speed must be greater than zero!"); + public final @Nonnull GEOMiner setProcessingSpeed(int speed) { + Preconditions.checkArgument(speed > 0, "The speed must be greater than zero!"); this.processingSpeed = speed; return this; @@ -155,10 +156,10 @@ public final GEOMiner setProcessingSpeed(int speed) { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final GEOMiner setEnergyConsumption(int energyConsumption) { - Validate.isTrue(energyConsumption > 0, "The energy consumption must be greater than zero!"); - Validate.isTrue(energyCapacity > 0, "You must specify the capacity before you can set the consumption amount."); - Validate.isTrue(energyConsumption <= energyCapacity, "The energy consumption cannot be higher than the capacity (" + energyCapacity + ')'); + public final @Nonnull GEOMiner setEnergyConsumption(int energyConsumption) { + Preconditions.checkArgument(energyConsumption > 0, "The energy consumption must be greater than zero!"); + Preconditions.checkArgument(energyCapacity > 0, "You must specify the capacity before you can set the consumption amount."); + Preconditions.checkArgument(energyConsumption <= energyCapacity, "The energy consumption cannot be higher than the capacity (" + energyCapacity + ')'); this.energyConsumedPerTick = energyConsumption; return this; @@ -188,19 +189,17 @@ public void register(@Nonnull SlimefunAddon addon) { } } - @Nonnull - private BlockPlaceHandler onBlockPlace() { + private @Nonnull BlockPlaceHandler onBlockPlace() { return new BlockPlaceHandler(false) { @Override - public void onPlayerPlace(BlockPlaceEvent e) { + public void onPlayerPlace(@Nonnull BlockPlaceEvent e) { updateHologram(e.getBlock(), "&7Idling..."); } }; } - @Nonnull - private BlockBreakHandler onBlockBreak() { + private @Nonnull BlockBreakHandler onBlockBreak() { return new SimpleBlockBreakHandler() { @Override @@ -217,21 +216,18 @@ public void onBlockBreak(@Nonnull Block b) { }; } - @Nonnull @Override - public int[] getInputSlots() { + public @Nonnull int[] getInputSlots() { return new int[0]; } - @Nonnull @Override - public int[] getOutputSlots() { + public @Nonnull int[] getOutputSlots() { return OUTPUT_SLOTS; } - @Nonnull @Override - public List getDisplayRecipes() { + public @Nonnull List getDisplayRecipes() { List displayRecipes = new LinkedList<>(); for (GEOResource resource : Slimefun.getRegistry().getGEOResources().values()) { @@ -249,7 +245,7 @@ public List getDisplayRecipes() { } @Override - public EnergyNetComponentType getEnergyComponentType() { + public @Nonnull EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.CONSUMER; } @@ -274,7 +270,7 @@ public boolean onClick(Player p, int slot, ItemStack cursor, ClickAction action) @Override public boolean onClick(InventoryClickEvent e, Player p, int slot, ItemStack cursor, ClickAction action) { - return cursor == null || cursor.getType() == null || cursor.getType() == Material.AIR; + return cursor == null || cursor.getType() == Material.AIR; } }); } @@ -328,7 +324,7 @@ private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { if (resource.isObtainableFromGEOMiner()) { OptionalInt optional = Slimefun.getGPSNetwork().getResourceManager().getSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4); - if (!optional.isPresent()) { + if (optional.isEmpty()) { updateHologram(b, "&4GEO-Scan required!"); return; } @@ -339,7 +335,7 @@ private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { return; } - processor.startOperation(b, new MiningOperation(resource.getItem().clone(), PROCESSING_TIME)); + processor.startOperation(b, new MiningOperation(resource, b, PROCESSING_TIME)); Slimefun.getGPSNetwork().getResourceManager().setSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4, supplies - 1); updateHologram(b, "&7Mining: &r" + resource.getName()); return; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java index 2a1201855f..a7dd33395c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java @@ -1,12 +1,18 @@ package io.github.thebusybiscuit.slimefun4.implementation.operations; +import java.util.OptionalInt; + import javax.annotation.Nonnull; -import org.apache.commons.lang.Validate; +import com.google.common.base.Preconditions; + +import org.bukkit.block.Block; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource; +import io.github.thebusybiscuit.slimefun4.api.geo.ResourceManager; import io.github.thebusybiscuit.slimefun4.core.machines.MachineOperation; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.geo.GEOMiner; /** @@ -22,20 +28,24 @@ public class MiningOperation implements MachineOperation { private final ItemStack result; + private final GEOResource resource; + private final Block block; private final int totalTicks; private int currentTicks = 0; - public MiningOperation(@Nonnull ItemStack result, int totalTicks) { - Validate.notNull(result, "The result cannot be null"); - Validate.isTrue(totalTicks >= 0, "The amount of total ticks must be a positive integer or zero, received: " + totalTicks); + public MiningOperation(@Nonnull GEOResource resource, Block block, int totalTicks) { + Preconditions.checkArgument(resource != null, "The resource cannot be null"); + Preconditions.checkArgument(totalTicks >= 0, "The amount of total ticks must be a positive integer or zero, received: " + totalTicks); - this.result = result; + this.resource = resource; + this.result = resource.getItem().clone(); + this.block = block; this.totalTicks = totalTicks; } @Override public void addProgress(int num) { - Validate.isTrue(num > 0, "Progress must be positive."); + Preconditions.checkArgument(num > 0, "Progress must be positive."); currentTicks += num; } @@ -59,4 +69,11 @@ public int getTotalTicks() { return totalTicks; } + @Override + public void cancel() { + ResourceManager resourceManager = Slimefun.getGPSNetwork().getResourceManager(); + OptionalInt supplies = resourceManager.getSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4); + supplies.ifPresent(s -> resourceManager.setSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4, s + 1)); + } + } From 65819d1784d9c41a0a0fd64c669312c6f92eded3 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sun, 20 Aug 2023 16:09:48 +0200 Subject: [PATCH 03/66] don't break MiningOperation, instead create new class --- .../implementation/items/geo/GEOMiner.java | 12 ++--- .../operations/GEOMiningOperation.java | 46 +++++++++++++++++++ .../operations/MiningOperation.java | 30 ++---------- 3 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index 184215a2a8..06f41a17a9 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -35,7 +35,7 @@ import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; -import io.github.thebusybiscuit.slimefun4.implementation.operations.MiningOperation; +import io.github.thebusybiscuit.slimefun4.implementation.operations.GEOMiningOperation; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; @@ -54,7 +54,7 @@ * * @see GEOResource */ -public class GEOMiner extends SlimefunItem implements RecipeDisplayItem, EnergyNetComponent, InventoryBlock, HologramOwner, MachineProcessHolder { +public class GEOMiner extends SlimefunItem implements RecipeDisplayItem, EnergyNetComponent, InventoryBlock, HologramOwner, MachineProcessHolder { private static final int[] BORDER = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 26, 27, 35, 36, 44, 45, 53 }; private static final int[] OUTPUT_BORDER = { 19, 20, 21, 22, 23, 24, 25, 28, 34, 37, 43, 46, 47, 48, 49, 50, 51, 52 }; @@ -62,7 +62,7 @@ public class GEOMiner extends SlimefunItem implements RecipeDisplayItem, EnergyN private static final int PROCESSING_TIME = 14; - private final MachineProcessor processor = new MachineProcessor<>(this); + private final MachineProcessor processor = new MachineProcessor<>(this); private int energyConsumedPerTick = -1; private int energyCapacity = -1; @@ -78,7 +78,7 @@ public GEOMiner(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeTy } @Override - public @Nonnull MachineProcessor getMachineProcessor() { + public @Nonnull MachineProcessor getMachineProcessor() { return processor; } @@ -294,7 +294,7 @@ public boolean isSynchronized() { protected void tick(@Nonnull Block b) { BlockMenu inv = BlockStorage.getInventory(b); - MiningOperation operation = processor.getOperation(b); + GEOMiningOperation operation = processor.getOperation(b); if (operation != null) { if (!operation.isFinished()) { @@ -335,7 +335,7 @@ private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { return; } - processor.startOperation(b, new MiningOperation(resource, b, PROCESSING_TIME)); + processor.startOperation(b, new GEOMiningOperation(resource, b, PROCESSING_TIME)); Slimefun.getGPSNetwork().getResourceManager().setSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4, supplies - 1); updateHologram(b, "&7Mining: &r" + resource.getName()); return; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java new file mode 100644 index 0000000000..ee981eac83 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java @@ -0,0 +1,46 @@ +package io.github.thebusybiscuit.slimefun4.implementation.operations; + +import java.util.OptionalInt; + +import javax.annotation.Nonnull; + +import org.bukkit.block.Block; + +import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource; +import io.github.thebusybiscuit.slimefun4.api.geo.ResourceManager; +import io.github.thebusybiscuit.slimefun4.core.machines.MachineOperation; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.implementation.items.geo.GEOMiner; + +/** + * This {@link MachineOperation} represents a {@link GEOMiner} + * mining a {@link GEOResource}. + * + * @author iTwins + * + * @see GEOMiner + * + */ +public class GEOMiningOperation extends MiningOperation { + + private final GEOResource resource; + private final Block block; + + public GEOMiningOperation(@Nonnull GEOResource resource, @Nonnull Block block, int totalTicks) { + super(resource.getItem().clone(), totalTicks); + this.resource = resource; + this.block = block; + } + + /** + * This returns the {@link GEOResource} back to the chunk + * when the {@link GEOMiningOperation} gets cancelled + */ + @Override + public void cancel() { + ResourceManager resourceManager = Slimefun.getGPSNetwork().getResourceManager(); + OptionalInt supplies = resourceManager.getSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4); + supplies.ifPresent(s -> resourceManager.setSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4, s + 1)); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java index a7dd33395c..83480ce63f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java @@ -1,45 +1,32 @@ package io.github.thebusybiscuit.slimefun4.implementation.operations; -import java.util.OptionalInt; - import javax.annotation.Nonnull; import com.google.common.base.Preconditions; -import org.bukkit.block.Block; import org.bukkit.inventory.ItemStack; -import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource; -import io.github.thebusybiscuit.slimefun4.api.geo.ResourceManager; import io.github.thebusybiscuit.slimefun4.core.machines.MachineOperation; -import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; -import io.github.thebusybiscuit.slimefun4.implementation.items.geo.GEOMiner; /** - * This {@link MachineOperation} represents a {@link GEOMiner} - * mining a {@link GEOResource}. + * This {@link MachineOperation} represents an operation + * with no inputs, only a result. * * @author TheBusyBiscuit - * - * @see GEOMiner * */ public class MiningOperation implements MachineOperation { private final ItemStack result; - private final GEOResource resource; - private final Block block; private final int totalTicks; private int currentTicks = 0; - public MiningOperation(@Nonnull GEOResource resource, Block block, int totalTicks) { - Preconditions.checkArgument(resource != null, "The resource cannot be null"); + public MiningOperation(@Nonnull ItemStack result, int totalTicks) { + Preconditions.checkArgument(result != null, "The result cannot be null"); Preconditions.checkArgument(totalTicks >= 0, "The amount of total ticks must be a positive integer or zero, received: " + totalTicks); - this.resource = resource; - this.result = resource.getItem().clone(); - this.block = block; + this.result = result; this.totalTicks = totalTicks; } @@ -69,11 +56,4 @@ public int getTotalTicks() { return totalTicks; } - @Override - public void cancel() { - ResourceManager resourceManager = Slimefun.getGPSNetwork().getResourceManager(); - OptionalInt supplies = resourceManager.getSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4); - supplies.ifPresent(s -> resourceManager.setSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4, s + 1)); - } - } From be3acf3edca66aba8b91a6dd9946f2a697e8f6a4 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sun, 20 Aug 2023 16:46:41 +0200 Subject: [PATCH 04/66] renamed cancel to onCancel --- .../slimefun4/core/machines/MachineOperation.java | 2 +- .../slimefun4/core/machines/MachineProcessor.java | 2 +- .../slimefun4/implementation/operations/GEOMiningOperation.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java index 67fda66d29..ff211b4594 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java @@ -61,6 +61,6 @@ default boolean isFinished() { * This method is called when a {@link MachineOperation} is interrupted before finishing. * Implement to specify behaviour that should happen in this case. */ - default void cancel() {} + default void onCancel() {} } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index 4ccf41192c..f8064c5c78 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -231,7 +231,7 @@ public boolean endOperation(@Nonnull BlockPosition pos) { Event event = new AsyncMachineOperationFinishEvent(pos, this, operation); Bukkit.getPluginManager().callEvent(event); } else { - operation.cancel(); + operation.onCancel(); } return true; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java index ee981eac83..664f4d669a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java @@ -37,7 +37,7 @@ public GEOMiningOperation(@Nonnull GEOResource resource, @Nonnull Block block, i * when the {@link GEOMiningOperation} gets cancelled */ @Override - public void cancel() { + public void onCancel() { ResourceManager resourceManager = Slimefun.getGPSNetwork().getResourceManager(); OptionalInt supplies = resourceManager.getSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4); supplies.ifPresent(s -> resourceManager.setSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4, s + 1)); From 6f194a6249cdf048319bf1ef59106b81b6fcdd1a Mon Sep 17 00:00:00 2001 From: Jeroen <48769316+iTwins@users.noreply.github.com> Date: Fri, 25 Aug 2023 18:14:47 +0200 Subject: [PATCH 05/66] Suggestion: remove empty line Co-authored-by: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> --- .../slimefun4/implementation/operations/GEOMiningOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java index 664f4d669a..912e05e5b2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java @@ -19,7 +19,6 @@ * @author iTwins * * @see GEOMiner - * */ public class GEOMiningOperation extends MiningOperation { From 150258b7db71309adf642bdb7fb1a227f9178937 Mon Sep 17 00:00:00 2001 From: ybw0014 Date: Sun, 27 Aug 2023 23:44:45 -0400 Subject: [PATCH 06/66] feat: allow sword of beheading drop piglin heads --- .../items/weapons/SwordOfBeheading.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java index 42842880cc..ef74985c70 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java @@ -8,6 +8,7 @@ import org.bukkit.Material; import org.bukkit.entity.Creeper; import org.bukkit.entity.Monster; +import org.bukkit.entity.Piglin; import org.bukkit.entity.Player; import org.bukkit.entity.Skeleton; import org.bukkit.entity.WitherSkeleton; @@ -26,7 +27,8 @@ /** * The {@link SwordOfBeheading} is a special kind of sword which allows you to obtain - * {@link Zombie}, {@link Skeleton} and {@link Creeper} skulls when killing the respective {@link Monster}. + * {@link Zombie}, {@link Skeleton}, {@link Creeper} and {@link Piglin} skulls when killing the respective + * {@link Monster}. * Additionally, you can also obtain the head of a {@link Player} by killing them too. * This sword also allows you to have a higher chance of getting the skull of a {@link WitherSkeleton} too. * @@ -41,13 +43,14 @@ public class SwordOfBeheading extends SimpleSlimefunItem { private final ItemSetting chanceSkeleton = new IntRangeSetting(this, "chance.SKELETON", 0, 40, 100); private final ItemSetting chanceWitherSkeleton = new IntRangeSetting(this, "chance.WITHER_SKELETON", 0, 25, 100); private final ItemSetting chanceCreeper = new IntRangeSetting(this, "chance.CREEPER", 0, 40, 100); + private final ItemSetting chancePiglin = new IntRangeSetting(this, "chance.PIGLIN", 0, 40, 100); private final ItemSetting chancePlayer = new IntRangeSetting(this, "chance.PLAYER", 0, 70, 100); @ParametersAreNonnullByDefault public SwordOfBeheading(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(itemGroup, item, recipeType, recipe); - addItemSetting(chanceZombie, chanceSkeleton, chanceWitherSkeleton, chanceCreeper, chancePlayer); + addItemSetting(chanceZombie, chanceSkeleton, chanceWitherSkeleton, chanceCreeper, chancePiglin, chancePlayer); } @Override @@ -56,27 +59,32 @@ public EntityKillHandler getItemHandler() { Random random = ThreadLocalRandom.current(); switch (e.getEntityType()) { - case ZOMBIE: + case ZOMBIE -> { if (random.nextInt(100) < chanceZombie.getValue()) { e.getDrops().add(new ItemStack(Material.ZOMBIE_HEAD)); } - break; - case SKELETON: + } + case SKELETON -> { if (random.nextInt(100) < chanceSkeleton.getValue()) { e.getDrops().add(new ItemStack(Material.SKELETON_SKULL)); } - break; - case CREEPER: + } + case CREEPER -> { if (random.nextInt(100) < chanceCreeper.getValue()) { e.getDrops().add(new ItemStack(Material.CREEPER_HEAD)); } - break; - case WITHER_SKELETON: + } + case WITHER_SKELETON -> { if (random.nextInt(100) < chanceWitherSkeleton.getValue()) { e.getDrops().add(new ItemStack(Material.WITHER_SKELETON_SKULL)); } - break; - case PLAYER: + } + case PIGLIN -> { + if (random.nextInt(100) < chancePiglin.getValue()) { + e.getDrops().add(new ItemStack(Material.PIGLIN_HEAD)); + } + } + case PLAYER -> { if (random.nextInt(100) < chancePlayer.getValue()) { ItemStack skull = new ItemStack(Material.PLAYER_HEAD); @@ -86,9 +94,9 @@ public EntityKillHandler getItemHandler() { e.getDrops().add(skull); } - break; - default: - break; + } + default -> { + } } }; } From 2fd3d0e50c1f849ec175cbaa48436f343a2616be Mon Sep 17 00:00:00 2001 From: ybw0014 Date: Mon, 28 Aug 2023 01:54:01 -0400 Subject: [PATCH 07/66] chore: inline default --- .../implementation/items/weapons/SwordOfBeheading.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java index ef74985c70..17b91d534a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java @@ -95,8 +95,7 @@ public EntityKillHandler getItemHandler() { e.getDrops().add(skull); } } - default -> { - } + default -> {} } }; } From df479f1d5d62d1b9c25c6908782f97aed4720a40 Mon Sep 17 00:00:00 2001 From: Jeroen <48769316+iTwins@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:10:25 +0200 Subject: [PATCH 08/66] use String.valueOf instead of appending to empty string --- .../io/github/thebusybiscuit/slimefun4/core/debug/Debug.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java index cf7687dc9f..50a05c309e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/debug/Debug.java @@ -106,7 +106,7 @@ public static void log(@Nonnull String test, @Nonnull String msg, @Nonnull Objec while ((i = msg.indexOf('{', i)) != -1 && msg.charAt(i + 1) == '}') { // Substring up to the opening brace `{`, add the variable for this and add the rest of the message msg = msg.substring(0, i) + vars[idx] + msg.substring(i + 2); - i += ("" + vars[idx++]).length(); + i += String.valueOf(vars[idx++]).length(); } return msg; From c06e316b6abe22991c84af60df2b2bd91d108b44 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sun, 3 Sep 2023 14:27:28 +0200 Subject: [PATCH 09/66] cursor.getType() == null check --- .../slimefun4/implementation/items/geo/GEOMiner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index 06f41a17a9..951f56ac45 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -270,7 +270,7 @@ public boolean onClick(Player p, int slot, ItemStack cursor, ClickAction action) @Override public boolean onClick(InventoryClickEvent e, Player p, int slot, ItemStack cursor, ClickAction action) { - return cursor == null || cursor.getType() == Material.AIR; + return cursor == null || cursor.getType() == null || cursor.getType() == Material.AIR; } }); } From dc2cdadc3ca7e7e4ba2cc2919e3be496af20812d Mon Sep 17 00:00:00 2001 From: iTwins Date: Thu, 7 Sep 2023 02:29:15 +0200 Subject: [PATCH 10/66] pass BlockPosition to onCancel --- .../slimefun4/core/machines/MachineOperation.java | 3 ++- .../slimefun4/core/machines/MachineProcessor.java | 2 +- .../implementation/items/geo/GEOMiner.java | 2 +- .../operations/GEOMiningOperation.java | 13 +++++-------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java index ff211b4594..3da6569b6c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineOperation.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.core.machines; +import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineProcessHolder; /** @@ -61,6 +62,6 @@ default boolean isFinished() { * This method is called when a {@link MachineOperation} is interrupted before finishing. * Implement to specify behaviour that should happen in this case. */ - default void onCancel() {} + default void onCancel(BlockPosition position) {} } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index f8064c5c78..c1e69b39c8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -231,7 +231,7 @@ public boolean endOperation(@Nonnull BlockPosition pos) { Event event = new AsyncMachineOperationFinishEvent(pos, this, operation); Bukkit.getPluginManager().callEvent(event); } else { - operation.onCancel(); + operation.onCancel(pos); } return true; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index 951f56ac45..d0f8c21b62 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -335,7 +335,7 @@ private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { return; } - processor.startOperation(b, new GEOMiningOperation(resource, b, PROCESSING_TIME)); + processor.startOperation(b, new GEOMiningOperation(resource, PROCESSING_TIME)); Slimefun.getGPSNetwork().getResourceManager().setSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4, supplies - 1); updateHologram(b, "&7Mining: &r" + resource.getName()); return; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java index 912e05e5b2..372f9f502c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/GEOMiningOperation.java @@ -4,8 +4,7 @@ import javax.annotation.Nonnull; -import org.bukkit.block.Block; - +import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource; import io.github.thebusybiscuit.slimefun4.api.geo.ResourceManager; import io.github.thebusybiscuit.slimefun4.core.machines.MachineOperation; @@ -23,12 +22,10 @@ public class GEOMiningOperation extends MiningOperation { private final GEOResource resource; - private final Block block; - public GEOMiningOperation(@Nonnull GEOResource resource, @Nonnull Block block, int totalTicks) { + public GEOMiningOperation(@Nonnull GEOResource resource, int totalTicks) { super(resource.getItem().clone(), totalTicks); this.resource = resource; - this.block = block; } /** @@ -36,10 +33,10 @@ public GEOMiningOperation(@Nonnull GEOResource resource, @Nonnull Block block, i * when the {@link GEOMiningOperation} gets cancelled */ @Override - public void onCancel() { + public void onCancel(@Nonnull BlockPosition position) { ResourceManager resourceManager = Slimefun.getGPSNetwork().getResourceManager(); - OptionalInt supplies = resourceManager.getSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4); - supplies.ifPresent(s -> resourceManager.setSupplies(resource, block.getWorld(), block.getX() >> 4, block.getZ() >> 4, s + 1)); + OptionalInt supplies = resourceManager.getSupplies(resource, position.getWorld(), position.getChunkX(), position.getChunkZ()); + supplies.ifPresent(s -> resourceManager.setSupplies(resource, position.getWorld(), position.getChunkX(), position.getChunkZ(), s + 1)); } } From 051c51915e26de4f01961bfc180c5e37c40cda7d Mon Sep 17 00:00:00 2001 From: iTwins Date: Thu, 7 Sep 2023 05:33:42 +0200 Subject: [PATCH 11/66] fix sensitive blocks attached to sf blocks not dropping (1.19+) --- .../listeners/BlockListener.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 30593427ee..75a9cdba8c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -14,6 +14,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -25,6 +26,7 @@ import org.bukkit.inventory.meta.ItemMeta; import io.github.bakedlibs.dough.protection.Interaction; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.events.ExplosiveToolBreakBlocksEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockBreakEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockPlaceEvent; @@ -53,6 +55,8 @@ */ public class BlockListener implements Listener { + private static final BlockFace[] CARDINAL_BLOCKFACES = new BlockFace[]{BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.DOWN, BlockFace.UP}; + public BlockListener(@Nonnull Slimefun plugin) { plugin.getServer().getPluginManager().registerEvents(this, plugin); } @@ -152,6 +156,9 @@ public void onBlockBreak(BlockBreakEvent e) { } callBlockHandler(e, item, drops, sfItem); + + checkForSensitiveBlocks(e.getBlock(), 0); + dropItems(e, drops); } } @@ -222,6 +229,52 @@ private void dropItems(BlockBreakEvent e, List drops) { } } + /** + * This method checks recursively for any sensitive blocks + * that are no longer supported due to this block breaking + * + * @param block + * The {@link Block} in question + * @param c + * The amount of times this has been recursively called + */ + @ParametersAreNonnullByDefault + private void checkForSensitiveBlocks(Block block, Integer c) { + if (c >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { + return; + } + for (BlockFace face : CARDINAL_BLOCKFACES) { + block.setType(Material.AIR, false); + if (!isSupported(block.getRelative(face).getBlockData(), block.getRelative(face))) { + Block relative = block.getRelative(face); + for (ItemStack drop : relative.getDrops()) { + block.getWorld().dropItemNaturally(relative.getLocation(), drop); + } + checkForSensitiveBlocks(relative, ++c); + } + } + } + + /** + * This method checks if the {@link BlockData} would be + * supported at the given {@link Block}. + * + * @param blockData + * The {@link BlockData} to check + * @param block + * The {@link Block} the {@link BlockData} would be at + * @return + * Whether the {@link BlockData} would be supported at the given {@link Block} + */ + private boolean isSupported(BlockData blockData, Block block) { + if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_19)) { + return blockData.isSupported(block); + } else { + // TODO: Make 1.16-1.18 version. BlockData::isSupported is 1.19+. + return true; + } + } + /** * This method checks for a sensitive {@link Block}. * Sensitive {@link Block Blocks} are pressure plates or saplings, which should be broken From 9c5114f9c274899923807b946a34f402895cfc45 Mon Sep 17 00:00:00 2001 From: iTwins Date: Thu, 7 Sep 2023 05:41:25 +0200 Subject: [PATCH 12/66] annotation --- .../slimefun4/implementation/listeners/BlockListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 75a9cdba8c..b919ac0980 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -266,6 +266,7 @@ private void checkForSensitiveBlocks(Block block, Integer c) { * @return * Whether the {@link BlockData} would be supported at the given {@link Block} */ + @ParametersAreNonnullByDefault private boolean isSupported(BlockData blockData, Block block) { if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_19)) { return blockData.isSupported(block); From 67fd5183f813a0e92ed38ae0cbf9ee9e7989d9cd Mon Sep 17 00:00:00 2001 From: ybw0014 Date: Thu, 7 Sep 2023 00:39:03 -0400 Subject: [PATCH 13/66] fix: add version check --- .../implementation/items/weapons/SwordOfBeheading.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java index 17b91d534a..9c9f500608 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java @@ -5,6 +5,8 @@ import javax.annotation.ParametersAreNonnullByDefault; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import org.bukkit.Material; import org.bukkit.entity.Creeper; import org.bukkit.entity.Monster; @@ -80,7 +82,8 @@ public EntityKillHandler getItemHandler() { } } case PIGLIN -> { - if (random.nextInt(100) < chancePiglin.getValue()) { + if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_20) && + random.nextInt(100) < chancePiglin.getValue()) { e.getDrops().add(new ItemStack(Material.PIGLIN_HEAD)); } } From 660f57b3b992fd767f9ce2f8a3c8f8e4829ff1ca Mon Sep 17 00:00:00 2001 From: ybw0014 Date: Thu, 7 Sep 2023 00:43:27 -0400 Subject: [PATCH 14/66] chore: fix import order --- .../implementation/items/weapons/SwordOfBeheading.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java index 9c9f500608..ce40980ded 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java @@ -5,8 +5,6 @@ import javax.annotation.ParametersAreNonnullByDefault; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; -import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import org.bukkit.Material; import org.bukkit.entity.Creeper; import org.bukkit.entity.Monster; @@ -19,12 +17,14 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.handlers.EntityKillHandler; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; /** From d4b6c0aac8d510fcc1bab2f19f877ce95c371822 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sat, 9 Sep 2023 16:46:14 +0200 Subject: [PATCH 15/66] renamed c to count --- .../slimefun4/implementation/listeners/BlockListener.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index b919ac0980..46152d988e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -235,12 +235,12 @@ private void dropItems(BlockBreakEvent e, List drops) { * * @param block * The {@link Block} in question - * @param c + * @param count * The amount of times this has been recursively called */ @ParametersAreNonnullByDefault - private void checkForSensitiveBlocks(Block block, Integer c) { - if (c >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { + private void checkForSensitiveBlocks(Block block, Integer count) { + if (count >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { return; } for (BlockFace face : CARDINAL_BLOCKFACES) { @@ -250,7 +250,7 @@ private void checkForSensitiveBlocks(Block block, Integer c) { for (ItemStack drop : relative.getDrops()) { block.getWorld().dropItemNaturally(relative.getLocation(), drop); } - checkForSensitiveBlocks(relative, ++c); + checkForSensitiveBlocks(relative, ++count); } } } From 7472552e1e45e8074247ff05f845ac1650f5ef67 Mon Sep 17 00:00:00 2001 From: iTwins Date: Sun, 8 Oct 2023 03:39:25 +0200 Subject: [PATCH 16/66] added compatibility for the 1.20 smithing table --- .../crafting/SmithingTableListener.java | 38 +++-- .../listeners/TestSmithingTableListener.java | 154 +++++++++++++++--- 2 files changed, 159 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SmithingTableListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SmithingTableListener.java index 745a63736a..dd1567f778 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SmithingTableListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SmithingTableListener.java @@ -2,14 +2,13 @@ import javax.annotation.Nonnull; -import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.ItemStack; +import org.bukkit.event.inventory.PrepareSmithingEvent; +import org.bukkit.event.inventory.SmithItemEvent; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; @@ -18,6 +17,7 @@ * smithing table. * * @author Sefiraat + * @author iTwins */ public class SmithingTableListener implements SlimefunCraftingListener { @@ -26,15 +26,29 @@ public SmithingTableListener(@Nonnull Slimefun plugin) { } @EventHandler(ignoreCancelled = true) - public void onSmith(InventoryClickEvent e) { - if (e.getInventory().getType() == InventoryType.SMITHING && e.getRawSlot() == 2 && e.getWhoClicked() instanceof Player) { - ItemStack materialItem = e.getInventory().getContents()[1]; - - // Checks if the item in the Material/Netherite slot is allowed to be used. - if (isUnallowed(materialItem)) { - e.setResult(Result.DENY); - Slimefun.getLocalization().sendMessage(e.getWhoClicked(), "smithing_table.not-working", true); + public void onSmith(SmithItemEvent e) { + SlimefunItem sfItem = SlimefunItem.getByItem(e.getInventory().getContents()[materialSlot()]); + if (sfItem != null && !sfItem.isUseableInWorkbench()) { + e.setResult(Result.DENY); + Slimefun.getLocalization().sendMessage(e.getWhoClicked(), "smithing_table.not-working", true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onPrepareSmith(PrepareSmithingEvent e) { + if (e.getInventory().getResult() != null) { + SlimefunItem sfItem = SlimefunItem.getByItem(e.getInventory().getContents()[materialSlot()]); + if (sfItem != null && !sfItem.isUseableInWorkbench()) { + e.setResult(null); } } } + + private int materialSlot() { + if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_20)) { + return 2; + } + return 1; + } + } diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestSmithingTableListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestSmithingTableListener.java index 472a90c9a1..3c57ed5060 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestSmithingTableListener.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestSmithingTableListener.java @@ -1,21 +1,26 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners; +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; + +import org.apache.commons.lang3.mutable.MutableObject; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryAction; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType.SlotType; -import org.bukkit.inventory.Inventory; +import org.bukkit.event.inventory.PrepareSmithingEvent; +import org.bukkit.event.inventory.SmithItemEvent; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.SmithingInventory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; @@ -24,15 +29,13 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.SmithingTableListener; import io.github.thebusybiscuit.slimefun4.test.TestUtilities; -import be.seeseemelk.mockbukkit.MockBukkit; -import be.seeseemelk.mockbukkit.ServerMock; - class TestSmithingTableListener { private static SmithingTableListener listener; private static ServerMock server; private static SlimefunItem slimefunIngot; + private static SlimefunItem usableSlimefunIngot; private static SlimefunItem slimefunTool; private static VanillaItem vanillaIngot; private static VanillaItem vanillaTool; @@ -45,11 +48,15 @@ public static void load() { slimefunTool = TestUtilities.mockSlimefunItem(plugin, "MOCK_DIAMOND_SWORD", new CustomItemStack(Material.DIAMOND_SWORD, "&6Mock")); slimefunIngot = TestUtilities.mockSlimefunItem(plugin, "MOCK_NETHERITE_INGOT", new CustomItemStack(Material.NETHERITE_INGOT, "&6Mock")); + usableSlimefunIngot = TestUtilities.mockSlimefunItem(plugin, "MOCK_NETHERITE_INGOT_USABLE", new CustomItemStack(Material.NETHERITE_INGOT, "&6Mock")); + usableSlimefunIngot.setUseableInWorkbench(true); + vanillaTool = TestUtilities.mockVanillaItem(plugin, Material.DIAMOND_SWORD, true); vanillaIngot = TestUtilities.mockVanillaItem(plugin, Material.NETHERITE_INGOT, true); slimefunTool.register(plugin); slimefunIngot.register(plugin); + usableSlimefunIngot.register(plugin); vanillaTool.register(plugin); vanillaIngot.register(plugin); } @@ -59,77 +66,182 @@ public static void unload() { MockBukkit.unmock(); } - private InventoryClickEvent mockSmithingEvent(ItemStack tool, ItemStack material) { + private SmithItemEvent mockSmithingEvent(ItemStack tool, ItemStack material) { Player player = server.addPlayer(); - Inventory inv = TestUtilities.mockInventory(InventoryType.SMITHING, tool, material, null); + + SmithingInventory inv = Mockito.mock(SmithingInventory.class); + // MinecraftVersion#isAtLeast always returns true during unit test, so we use the 1.20 layout here. + Mockito.when(inv.getContents()).thenReturn(new ItemStack[] { new ItemStack(Material.NETHERITE_UPGRADE_SMITHING_TEMPLATE), tool, material, null }); + InventoryView view = player.openInventory(inv); - InventoryClickEvent event = new InventoryClickEvent(view, SlotType.CONTAINER, 2, ClickType.LEFT, InventoryAction.PICKUP_ONE); + SmithItemEvent event = new SmithItemEvent(view, SlotType.RESULT, 3, ClickType.LEFT, InventoryAction.PICKUP_ONE); listener.onSmith(event); return event; } + private PrepareSmithingEvent mockPrepareSmithingEvent(ItemStack tool, ItemStack material) { + Player player = server.addPlayer(); + + SmithingInventory inv = Mockito.mock(SmithingInventory.class); + MutableObject result = new MutableObject<>(new ItemStack(Material.NETHERITE_PICKAXE)); + + Mockito.doAnswer(invocation -> { + ItemStack argument = invocation.getArgument(0); + result.setValue(argument); + return null; + }).when(inv).setResult(Mockito.any()); + + Mockito.when(inv.getResult()).thenAnswer(invocation -> result.getValue()); + // MinecraftVersion#isAtLeast always returns true during unit test, so we use the 1.20 layout here. + Mockito.when(inv.getContents()).thenReturn(new ItemStack[] { new ItemStack(Material.NETHERITE_UPGRADE_SMITHING_TEMPLATE), tool, material, null }); + + InventoryView view = player.openInventory(inv); + PrepareSmithingEvent event = new PrepareSmithingEvent(view, result.getValue()); + + listener.onPrepareSmith(event); + return event; + } + @Test @DisplayName("Test that vanilla is unchanged (ItemStack tool x ItemStack material)") void testSmithingTableWithItemStacks() { - InventoryClickEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), new ItemStack(Material.NETHERITE_INGOT)); + SmithItemEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), new ItemStack(Material.NETHERITE_INGOT)); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } @Test @DisplayName("Test that SlimefunItem material doesn't work (ItemStack tool x SlimefunItem material)") void testSmithingTableWithItemStackAndSlimefunItem() { - InventoryClickEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), slimefunIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), slimefunIngot.getItem()); Assertions.assertEquals(Result.DENY, event.getResult()); } @Test @DisplayName("Test that VanillaItem material works (ItemStack tool x VanillaItem material)") void testSmithingTableWithItemStackAndVanillaItem() { - InventoryClickEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), vanillaIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), vanillaIngot.getItem()); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } @Test @DisplayName("Test that SlimefunItems can upgrade with vanilla (SlimefunItem tool x ItemStack material)") void testSmithingTableWithSlimefunItemAndItemStack() { - InventoryClickEvent event = mockSmithingEvent(slimefunTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); + SmithItemEvent event = mockSmithingEvent(slimefunTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } @Test @DisplayName("Test that SlimefunItems can't upgrade with SlimefunItem materials (SlimefunItem tool x SlimefunItem material)") void testSmithingTableWithSlimefunItems() { - InventoryClickEvent event = mockSmithingEvent(slimefunTool.getItem(), slimefunIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(slimefunTool.getItem(), slimefunIngot.getItem()); Assertions.assertEquals(Result.DENY, event.getResult()); } @Test @DisplayName("Test that SlimefunItems can upgrade with VanillaItems (SlimefunItem tool x VanillaItem material)") void testSmithingTableWithSlimefunItemAndVanillaItem() { - InventoryClickEvent event = mockSmithingEvent(slimefunTool.getItem(), vanillaIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(slimefunTool.getItem(), vanillaIngot.getItem()); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } @Test - @DisplayName("Test that SlimefunItems can upgrade with vanilla (SlimefunItem tool x ItemStack material)") + @DisplayName("Test that VanillaItems can upgrade with vanilla (VanillaItem tool x ItemStack material)") void testSmithingTableWithVanillaItemAndItemStack() { - InventoryClickEvent event = mockSmithingEvent(vanillaTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); + SmithItemEvent event = mockSmithingEvent(vanillaTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } @Test - @DisplayName("Test that SlimefunItems can't upgrade with SlimefunItem materials (SlimefunItem tool x SlimefunItem material)") + @DisplayName("Test that VanillaItems can't upgrade with SlimefunItem materials (VanillaItem tool x SlimefunItem material)") void testSmithingTableWithVanillaItemAndSlimefunItem() { - InventoryClickEvent event = mockSmithingEvent(vanillaTool.getItem(), slimefunIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(vanillaTool.getItem(), slimefunIngot.getItem()); Assertions.assertEquals(Result.DENY, event.getResult()); } @Test - @DisplayName("Test that SlimefunItems can upgrade with VanillaItems (SlimefunItem tool x VanillaItem material)") + @DisplayName("Test that VanillaItems can upgrade with VanillaItems (VanillaItem tool x VanillaItem material)") void testSmithingTableWithVanillaItemAndVanillaItem() { - InventoryClickEvent event = mockSmithingEvent(vanillaTool.getItem(), vanillaIngot.getItem()); + SmithItemEvent event = mockSmithingEvent(vanillaTool.getItem(), vanillaIngot.getItem()); Assertions.assertEquals(Result.DEFAULT, event.getResult()); } + @Test + @DisplayName("Test that ItemStacks can be upgraded with SlimefunItem can-be-used-in-workbenches: true") + void testCanBeUsedInWorkbenchTrue() { + Assertions.assertTrue(usableSlimefunIngot.isUseableInWorkbench()); + SmithItemEvent event = mockSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), usableSlimefunIngot.getItem()); + Assertions.assertEquals(Result.DEFAULT, event.getResult()); + } + + @Test + @DisplayName("Test that vanilla is unchanged (ItemStack tool x ItemStack material)") + void testPrepareSmithingTableWithItemStacks() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), new ItemStack(Material.NETHERITE_INGOT)); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that SlimefunItem material doesn't work (ItemStack tool x SlimefunItem material)") + void testPrepareSmithingTableWithItemStackAndSlimefunItem() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), slimefunIngot.getItem()); + Assertions.assertNull(event.getResult()); + } + + @Test + @DisplayName("Test that VanillaItem material works (ItemStack tool x VanillaItem material)") + void testPrepareSmithingTableWithItemStackAndVanillaItem() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), vanillaIngot.getItem()); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that SlimefunItems can upgrade with vanilla (SlimefunItem tool x ItemStack material)") + void testPrepareSmithingTableWithSlimefunItemAndItemStack() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(slimefunTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that SlimefunItems can't upgrade with SlimefunItem materials (SlimefunItem tool x SlimefunItem material)") + void testPrepareSmithingTableWithSlimefunItems() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(slimefunTool.getItem(), slimefunIngot.getItem()); + Assertions.assertNull(event.getResult()); + } + + @Test + @DisplayName("Test that SlimefunItems can upgrade with VanillaItems (SlimefunItem tool x VanillaItem material)") + void testPrepareSmithingTableWithSlimefunItemAndVanillaItem() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(slimefunTool.getItem(), vanillaIngot.getItem()); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that VanillaItems can upgrade with vanilla (VanillaItem tool x ItemStack material)") + void testPrepareSmithingTableWithVanillaItemAndItemStack() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(vanillaTool.getItem(), new ItemStack(Material.NETHERITE_INGOT)); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that VanillaItems can't upgrade with SlimefunItem materials (VanillaItem tool x SlimefunItem material)") + void testPrepareSmithingTableWithVanillaItemAndSlimefunItem() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(vanillaTool.getItem(), slimefunIngot.getItem()); + Assertions.assertNull(event.getResult()); + } + + @Test + @DisplayName("Test that VanillaItems can upgrade with VanillaItems (VanillaItem tool x VanillaItem material)") + void testPrepareSmithingTableWithVanillaItemAndVanillaItem() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(vanillaTool.getItem(), vanillaIngot.getItem()); + Assertions.assertNotNull(event.getResult()); + } + + @Test + @DisplayName("Test that ItemStacks can be upgraded with SlimefunItem can-be-used-in-workbenches: true") + void testPrepareCanBeUsedInWorkbenchTrue() { + PrepareSmithingEvent event = mockPrepareSmithingEvent(new ItemStack(Material.DIAMOND_SWORD), usableSlimefunIngot.getItem()); + Assertions.assertNotNull(event.getResult()); + } + } From 9cc955d75eab4f584418998d8567f92736590ce4 Mon Sep 17 00:00:00 2001 From: Silent Date: Sun, 15 Oct 2023 01:39:20 -0700 Subject: [PATCH 17/66] Optional player for SlimefunItemSpawnEvent --- .../api/events/SlimefunItemSpawnEvent.java | 24 +++++++++++++++++++ .../items/altar/AncientPedestal.java | 2 +- .../items/seasonal/ChristmasPresent.java | 2 +- .../implementation/items/tools/GoldPan.java | 2 +- .../items/tools/PickaxeOfContainment.java | 2 +- .../slimefun4/utils/SlimefunUtils.java | 17 +++++++++++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java index 27f544e7e8..14dc2b4abf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java @@ -1,10 +1,14 @@ package io.github.thebusybiscuit.slimefun4.api.events; +import java.util.Optional; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; import org.bukkit.Location; +import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -29,6 +33,7 @@ public class SlimefunItemSpawnEvent extends Event implements Cancellable { private ItemStack itemStack; private boolean cancelled; private final ItemSpawnReason itemSpawnReason; + private final Player player; @ParametersAreNonnullByDefault public SlimefunItemSpawnEvent(Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { @@ -36,6 +41,25 @@ public SlimefunItemSpawnEvent(Location location, ItemStack itemStack, ItemSpawnR this.itemStack = itemStack; this.itemSpawnReason = itemSpawnReason; this.cancelled = false; + this.player = null; + } + + @ParametersAreNonnullByDefault + public SlimefunItemSpawnEvent(@Nullable Player player, Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { + this.location = location; + this.itemStack = itemStack; + this.itemSpawnReason = itemSpawnReason; + this.cancelled = false; + this.player = player; + } + + /** + * Optionally returns the {@link Player} responsible for this spawn reason. + * + * @return The player responsible if applicable. + */ + public Optional getPlayer() { + return Optional.ofNullable(player); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java index 99a7b15585..f0a4fa95c0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java @@ -167,7 +167,7 @@ public void placeItem(@Nonnull Player p, @Nonnull Block b) { ItemUtils.consumeItem(hand, false); } - Item entity = SlimefunUtils.spawnItem(b.getLocation().add(0.5, 1.2, 0.5), displayItem, ItemSpawnReason.ANCIENT_PEDESTAL_PLACE_ITEM); + Item entity = SlimefunUtils.spawnItem(p, b.getLocation().add(0.5, 1.2, 0.5), displayItem, ItemSpawnReason.ANCIENT_PEDESTAL_PLACE_ITEM, false); if (entity != null) { ArmorStand armorStand = getArmorStand(b, true); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java index f4cc010628..3d16205a8f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java @@ -55,7 +55,7 @@ public ChristmasPresent(ItemGroup itemGroup, SlimefunItemStack item, RecipeType Block b = block.getRelative(e.getClickedFace()); ItemStack gift = gifts[ThreadLocalRandom.current().nextInt(gifts.length)].clone(); - SlimefunUtils.spawnItem(b.getLocation(), gift, ItemSpawnReason.CHRISTMAS_PRESENT_OPENED, true); + SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), gift, ItemSpawnReason.CHRISTMAS_PRESENT_OPENED, true); }); }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java index 01478526cb..d7c0fb52b2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java @@ -154,7 +154,7 @@ public void updateRandomizer() { // Make sure that the randomly selected item is not air if (output.getType() != Material.AIR) { - SlimefunUtils.spawnItem(b.getLocation(), output.clone(), ItemSpawnReason.GOLD_PAN_USE, true); + SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), output.clone(), ItemSpawnReason.GOLD_PAN_USE, true); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java index c8064299a0..6be4cc5d71 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java @@ -50,7 +50,7 @@ public PickaxeOfContainment(ItemGroup itemGroup, SlimefunItemStack item, RecipeT if (b.getType() == Material.SPAWNER) { ItemStack spawner = breakSpawner(b); - SlimefunUtils.spawnItem(b.getLocation(), spawner, ItemSpawnReason.BROKEN_SPAWNER_DROP, true); + SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), spawner, ItemSpawnReason.BROKEN_SPAWNER_DROP, true); e.setExpToDrop(0); e.setDropItems(false); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java index f57aaf4ac7..ada4a7db6f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -610,6 +610,23 @@ public static boolean canPlayerUseItem(@Nonnull Player p, @Nullable ItemStack it } } + public static @Nullable Item spawnItem(Player player, Location loc, ItemStack item, ItemSpawnReason reason, boolean addRandomOffset) { + SlimefunItemSpawnEvent event = new SlimefunItemSpawnEvent(player, loc, item, reason); + Slimefun.instance().getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + World world = event.getLocation().getWorld(); + + if (addRandomOffset) { + return world.dropItemNaturally(event.getLocation(), event.getItemStack()); + } else { + return world.dropItem(event.getLocation(), event.getItemStack()); + } + } else { + return null; + } + } + /** * Helper method to spawn an {@link ItemStack}. * This method automatically calls a {@link SlimefunItemSpawnEvent} to allow From bd4ca3e72fe156b20dfb9913943bc39f89d42cbe Mon Sep 17 00:00:00 2001 From: Silent <46107752+TheSilentPro@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:47:31 -0700 Subject: [PATCH 18/66] Update src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java Mark optional as nonnull Co-authored-by: TheBusyBiscuit --- .../slimefun4/api/events/SlimefunItemSpawnEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java index 14dc2b4abf..f7aa63f3e0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java @@ -58,7 +58,7 @@ public SlimefunItemSpawnEvent(@Nullable Player player, Location location, ItemSt * * @return The player responsible if applicable. */ - public Optional getPlayer() { + public @Nonnull Optional getPlayer() { return Optional.ofNullable(player); } From 5527729109108e7c262a38ee9c883d4bf9240854 Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 16 Oct 2023 14:24:10 -0700 Subject: [PATCH 19/66] Reduce repeated code --- .../api/events/SlimefunItemSpawnEvent.java | 12 ++---- .../items/altar/AncientPedestal.java | 2 +- .../items/seasonal/ChristmasPresent.java | 2 +- .../implementation/items/tools/GoldPan.java | 2 +- .../items/tools/PickaxeOfContainment.java | 2 +- .../slimefun4/utils/SlimefunUtils.java | 40 +++++++++++-------- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java index 14dc2b4abf..07d284050d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/SlimefunItemSpawnEvent.java @@ -36,21 +36,17 @@ public class SlimefunItemSpawnEvent extends Event implements Cancellable { private final Player player; @ParametersAreNonnullByDefault - public SlimefunItemSpawnEvent(Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { + public SlimefunItemSpawnEvent(@Nullable Player player, Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { this.location = location; this.itemStack = itemStack; this.itemSpawnReason = itemSpawnReason; this.cancelled = false; - this.player = null; + this.player = player; } @ParametersAreNonnullByDefault - public SlimefunItemSpawnEvent(@Nullable Player player, Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { - this.location = location; - this.itemStack = itemStack; - this.itemSpawnReason = itemSpawnReason; - this.cancelled = false; - this.player = player; + public SlimefunItemSpawnEvent(Location location, ItemStack itemStack, ItemSpawnReason itemSpawnReason) { + this(null, location, itemStack, itemSpawnReason); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java index f0a4fa95c0..bbf19d36d8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/altar/AncientPedestal.java @@ -167,7 +167,7 @@ public void placeItem(@Nonnull Player p, @Nonnull Block b) { ItemUtils.consumeItem(hand, false); } - Item entity = SlimefunUtils.spawnItem(p, b.getLocation().add(0.5, 1.2, 0.5), displayItem, ItemSpawnReason.ANCIENT_PEDESTAL_PLACE_ITEM, false); + Item entity = SlimefunUtils.spawnItem(b.getLocation().add(0.5, 1.2, 0.5), displayItem, ItemSpawnReason.ANCIENT_PEDESTAL_PLACE_ITEM, false, p); if (entity != null) { ArmorStand armorStand = getArmorStand(b, true); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java index 3d16205a8f..37ec6cc244 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java @@ -55,7 +55,7 @@ public ChristmasPresent(ItemGroup itemGroup, SlimefunItemStack item, RecipeType Block b = block.getRelative(e.getClickedFace()); ItemStack gift = gifts[ThreadLocalRandom.current().nextInt(gifts.length)].clone(); - SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), gift, ItemSpawnReason.CHRISTMAS_PRESENT_OPENED, true); + SlimefunUtils.spawnItem(b.getLocation(), gift, ItemSpawnReason.CHRISTMAS_PRESENT_OPENED, true, e.getPlayer()); }); }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java index d7c0fb52b2..08f69ec27a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java @@ -154,7 +154,7 @@ public void updateRandomizer() { // Make sure that the randomly selected item is not air if (output.getType() != Material.AIR) { - SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), output.clone(), ItemSpawnReason.GOLD_PAN_USE, true); + SlimefunUtils.spawnItem(b.getLocation(), output.clone(), ItemSpawnReason.GOLD_PAN_USE, true, e.getPlayer()); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java index 6be4cc5d71..86499f2bee 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java @@ -50,7 +50,7 @@ public PickaxeOfContainment(ItemGroup itemGroup, SlimefunItemStack item, RecipeT if (b.getType() == Material.SPAWNER) { ItemStack spawner = breakSpawner(b); - SlimefunUtils.spawnItem(e.getPlayer(), b.getLocation(), spawner, ItemSpawnReason.BROKEN_SPAWNER_DROP, true); + SlimefunUtils.spawnItem(b.getLocation(), spawner, ItemSpawnReason.BROKEN_SPAWNER_DROP, true, e.getPlayer()); e.setExpToDrop(0); e.setDropItems(false); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java index ada4a7db6f..be19a2b5ab 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -589,12 +589,14 @@ public static boolean canPlayerUseItem(@Nonnull Player p, @Nullable ItemStack it * The {@link ItemSpawnReason} why the item is being dropped * @param addRandomOffset * Whether a random offset should be added (see {@link World#dropItemNaturally(Location, ItemStack)}) + * @param player + * The player that caused this {@link org.bukkit.event.entity.ItemSpawnEvent} * * @return The dropped {@link Item} (or null if the {@link SlimefunItemSpawnEvent} was cancelled) */ @ParametersAreNonnullByDefault - public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason, boolean addRandomOffset) { - SlimefunItemSpawnEvent event = new SlimefunItemSpawnEvent(loc, item, reason); + public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason, boolean addRandomOffset, @Nullable Player player) { + SlimefunItemSpawnEvent event = new SlimefunItemSpawnEvent(player, loc, item, reason); Slimefun.instance().getServer().getPluginManager().callEvent(event); if (!event.isCancelled()) { @@ -610,21 +612,25 @@ public static boolean canPlayerUseItem(@Nonnull Player p, @Nullable ItemStack it } } - public static @Nullable Item spawnItem(Player player, Location loc, ItemStack item, ItemSpawnReason reason, boolean addRandomOffset) { - SlimefunItemSpawnEvent event = new SlimefunItemSpawnEvent(player, loc, item, reason); - Slimefun.instance().getServer().getPluginManager().callEvent(event); - - if (!event.isCancelled()) { - World world = event.getLocation().getWorld(); - - if (addRandomOffset) { - return world.dropItemNaturally(event.getLocation(), event.getItemStack()); - } else { - return world.dropItem(event.getLocation(), event.getItemStack()); - } - } else { - return null; - } + /** + * Helper method to spawn an {@link ItemStack}. + * This method automatically calls a {@link SlimefunItemSpawnEvent} to allow + * other plugins to catch the item being dropped. + * + * @param loc + * The {@link Location} where to drop the item + * @param item + * The {@link ItemStack} to drop + * @param reason + * The {@link ItemSpawnReason} why the item is being dropped + * @param addRandomOffset + * Whether a random offset should be added (see {@link World#dropItemNaturally(Location, ItemStack)}) + * + * @return The dropped {@link Item} (or null if the {@link SlimefunItemSpawnEvent} was cancelled) + */ + @ParametersAreNonnullByDefault + public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason, boolean addRandomOffset) { + return spawnItem(loc, item, reason, addRandomOffset, null); } /** From cb736d314e28b59f89f3f9ddf92ece2ca0e9e246 Mon Sep 17 00:00:00 2001 From: Silent Date: Mon, 16 Oct 2023 23:27:25 -0700 Subject: [PATCH 20/66] fix qualifier --- .../io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java index be19a2b5ab..5fc3bda47f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -590,7 +590,7 @@ public static boolean canPlayerUseItem(@Nonnull Player p, @Nullable ItemStack it * @param addRandomOffset * Whether a random offset should be added (see {@link World#dropItemNaturally(Location, ItemStack)}) * @param player - * The player that caused this {@link org.bukkit.event.entity.ItemSpawnEvent} + * The player that caused this {@link SlimefunItemSpawnEvent} * * @return The dropped {@link Item} (or null if the {@link SlimefunItemSpawnEvent} was cancelled) */ From 82b89673d8bb8c4ae2fe792fb23f3d45c11d38d2 Mon Sep 17 00:00:00 2001 From: Kub94ekCZ <145360194+Kub94ekCZ@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:43:48 +0200 Subject: [PATCH 21/66] Update ButcherAndroidListener.java Fixed bug with emerald drop from vindicators when killed by Butcher Android --- .../implementation/listeners/ButcherAndroidListener.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java index 1432edd1dd..793698641a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java @@ -88,5 +88,9 @@ private void addExtraDrops(List drops, EntityType entityType) { if (entityType == EntityType.BLAZE) { drops.add(new ItemStack(Material.BLAZE_ROD, 1 + random.nextInt(1))); } + + if (entityType == EntityType.VINDICATOR) { + drops.add(new Itemstack(Material.EMERALD, 1 + random.nextInt(2))); + } } } From 9135b3810ebf5622d494e75eb9442f0ecba67ca1 Mon Sep 17 00:00:00 2001 From: test137e29b Date: Fri, 10 Nov 2023 21:48:44 +0000 Subject: [PATCH 22/66] Add Yaw to GPS Waypoint Location --- .../slimefun4/implementation/items/gps/GPSMarkerTool.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java index d677eea5b5..dd3d2f7f48 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java @@ -36,7 +36,11 @@ public ItemUseHandler getItemHandler() { if (e.getClickedBlock().isPresent()) { Block b = e.getClickedBlock().get().getRelative(e.getClickedFace()); - Slimefun.getGPSNetwork().createWaypoint(e.getPlayer(), b.getLocation()); + Location l = b.getLocation(); + Location locationWithYaw = new Location( + l.getWorld(), l.getX(), l.getY(), l.getZ(), e.getPlayer().getLocation().getYaw(), 0.0f + ); + Slimefun.getGPSNetwork().createWaypoint(e.getPlayer(), locationWithYaw); } }; } From c8cb6ff7502114b8e09a6fbcdaf6906c26d966ef Mon Sep 17 00:00:00 2001 From: test137e29b Date: Sat, 11 Nov 2023 00:43:31 +0000 Subject: [PATCH 23/66] setYaw --- .../slimefun4/implementation/items/gps/GPSMarkerTool.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java index dd3d2f7f48..65c430237a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSMarkerTool.java @@ -37,10 +37,8 @@ public ItemUseHandler getItemHandler() { if (e.getClickedBlock().isPresent()) { Block b = e.getClickedBlock().get().getRelative(e.getClickedFace()); Location l = b.getLocation(); - Location locationWithYaw = new Location( - l.getWorld(), l.getX(), l.getY(), l.getZ(), e.getPlayer().getLocation().getYaw(), 0.0f - ); - Slimefun.getGPSNetwork().createWaypoint(e.getPlayer(), locationWithYaw); + l.setYaw(e.getPlayer().getLocation().getYaw()); + Slimefun.getGPSNetwork().createWaypoint(e.getPlayer(), l); } }; } From f48665da6d3668ecfa4c3972611a1cef03992737 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Sat, 18 Nov 2023 19:18:04 -0600 Subject: [PATCH 24/66] Fix the case of SlimefunItem#itemhandlers --- .../slimefun4/api/items/SlimefunItem.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java index d3bc8368fd..20a12ef6b7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java @@ -109,7 +109,7 @@ public class SlimefunItem implements Placeable { private Optional wikiURL = Optional.empty(); - private final OptionalMap, ItemHandler> itemhandlers = new OptionalMap<>(HashMap::new); + private final OptionalMap, ItemHandler> itemHandlers = new OptionalMap<>(HashMap::new); private final Set> itemSettings = new HashSet<>(); private boolean ticking = false; @@ -477,12 +477,12 @@ public void register(@Nonnull SlimefunAddon addon) { onEnable(); } else { // Clear item handlers if we are disabled so that calling them isn't possible later on - for (ItemHandler handler : this.itemhandlers.values()) { + for (ItemHandler handler : this.itemHandlers.values()) { if (handler instanceof BlockTicker) { Slimefun.getRegistry().getTickerBlocks().remove(getId()); } } - this.itemhandlers.clear(); + this.itemHandlers.clear(); } // Lock the SlimefunItemStack from any accidental manipulations @@ -540,7 +540,7 @@ private final void onEnable() { } private void loadItemHandlers() { - for (ItemHandler handler : itemhandlers.values()) { + for (ItemHandler handler : itemHandlers.values()) { Optional exception = handler.validate(this); // Check if the validation caused an exception. @@ -802,7 +802,7 @@ public final void addItemHandler(ItemHandler... handlers) { } for (ItemHandler handler : handlers) { - itemhandlers.put(handler.getIdentifier(), handler); + itemHandlers.put(handler.getIdentifier(), handler); // Tickers are a special case (at the moment at least) if (handler instanceof BlockTicker ticker) { @@ -914,7 +914,7 @@ public final void addOfficialWikipage(@Nonnull String page) { * @return The Set of item handlers */ public @Nonnull Collection getHandlers() { - return itemhandlers.values(); + return itemHandlers.values(); } /** @@ -932,7 +932,7 @@ public final void addOfficialWikipage(@Nonnull String page) { */ @ParametersAreNonnullByDefault public boolean callItemHandler(Class c, Consumer callable) { - Optional handler = itemhandlers.get(c); + Optional handler = itemHandlers.get(c); if (handler.isPresent()) { try { From ea9b2289aa8aa9296b8b1cf72ede4067abb6216f Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Tue, 21 Nov 2023 07:47:07 -0600 Subject: [PATCH 25/66] Fix typo --- .../implementation/listeners/ButcherAndroidListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java index 793698641a..9e63107680 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ButcherAndroidListener.java @@ -90,7 +90,7 @@ private void addExtraDrops(List drops, EntityType entityType) { } if (entityType == EntityType.VINDICATOR) { - drops.add(new Itemstack(Material.EMERALD, 1 + random.nextInt(2))); + drops.add(new ItemStack(Material.EMERALD, 1 + random.nextInt(2))); } } } From d87e26b47a662ab1c5d3466f17bc44210a91eca3 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 22 Nov 2023 20:38:30 +0100 Subject: [PATCH 26/66] replace builds page with blob builds --- .../slimefun4/core/services/UpdaterService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index bea541fc28..68259b6b64 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -5,6 +5,7 @@ import javax.annotation.Nonnull; +import io.github.bakedlibs.dough.updater.BlobBuildUpdater; import org.bukkit.plugin.Plugin; import io.github.bakedlibs.dough.config.Config; @@ -53,7 +54,7 @@ public class UpdaterService { */ public UpdaterService(@Nonnull Slimefun plugin, @Nonnull String version, @Nonnull File file) { this.plugin = plugin; - GitHubBuildsUpdater autoUpdater = null; + BlobBuildUpdater autoUpdater = null; if (version.contains("UNOFFICIAL")) { // This Server is using a modified build that is not a public release. @@ -61,7 +62,7 @@ public UpdaterService(@Nonnull Slimefun plugin, @Nonnull String version, @Nonnul } else if (version.startsWith("DEV - ")) { // If we are using a development build, we want to switch to our custom try { - autoUpdater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/master"); + autoUpdater = new BlobBuildUpdater(plugin, file, "Slimefun4", "Dev"); } catch (Exception x) { plugin.getLogger().log(Level.SEVERE, "Failed to create AutoUpdater", x); } @@ -70,7 +71,7 @@ public UpdaterService(@Nonnull Slimefun plugin, @Nonnull String version, @Nonnul } else if (version.startsWith("RC - ")) { // If we are using a "stable" build, we want to switch to our custom try { - autoUpdater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/stable", "RC - "); + autoUpdater = new BlobBuildUpdater(plugin, file, "Slimefun4", "RC"); } catch (Exception x) { plugin.getLogger().log(Level.SEVERE, "Failed to create AutoUpdater", x); } From 60a90d80f230a958b9722844ed00bd4dd07865dd Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 22 Nov 2023 21:05:53 +0100 Subject: [PATCH 27/66] removed unused import --- .../thebusybiscuit/slimefun4/core/services/UpdaterService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index 68259b6b64..00f0ce3bcf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -9,7 +9,6 @@ import org.bukkit.plugin.Plugin; import io.github.bakedlibs.dough.config.Config; -import io.github.bakedlibs.dough.updater.GitHubBuildsUpdater; import io.github.bakedlibs.dough.updater.PluginUpdater; import io.github.bakedlibs.dough.versions.PrefixedVersion; import io.github.thebusybiscuit.slimefun4.api.SlimefunBranch; From 7288cf3bf3bd6127ad540f9c3e21ede119e13f31 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 22 Nov 2023 21:17:06 +0100 Subject: [PATCH 28/66] pulled latest changes and changed the url --- .../thebusybiscuit/slimefun4/core/services/UpdaterService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index 00f0ce3bcf..ebd5f971e6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -16,7 +16,7 @@ /** * This Class represents our {@link PluginUpdater} Service. - * If enabled, it will automatically connect to https://thebusybiscuit.github.io/builds/ + * If enabled, it will automatically connect to https://blob.build/ * to check for updates and to download them automatically. * * @author TheBusyBiscuit From 6af85d9ab957ad07c9e0701b1f95182a2088962e Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Thu, 23 Nov 2023 06:30:35 -0600 Subject: [PATCH 29/66] Fix freezer material --- .../thebusybiscuit/slimefun4/implementation/SlimefunItems.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java index d65a67bd34..8695759316 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java @@ -830,7 +830,7 @@ private SlimefunItems() {} public static final SlimefunItemStack FREEZER = new SlimefunItemStack("FREEZER", Material.LIGHT_BLUE_STAINED_GLASS, "&bFreezer", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(18)); public static final SlimefunItemStack FREEZER_2 = new SlimefunItemStack("FREEZER_2", Material.LIGHT_BLUE_STAINED_GLASS, "&bFreezer &7(&eII&7)", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(2), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(30)); - public static final SlimefunItemStack FREEZER_3 = new SlimefunItemStack("FREEZER_3", Material.LIGHT_GRAY_STAINED_GLASS, "&bFreezer &7(&eIII&7)", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(3), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(42)); + public static final SlimefunItemStack FREEZER_3 = new SlimefunItemStack("FREEZER_3", Material.LIGHT_BLUE_STAINED_GLASS, "&bFreezer &7(&eIII&7)", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.speed(3), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(42)); public static final SlimefunItemStack ELECTRIC_GOLD_PAN = new SlimefunItemStack("ELECTRIC_GOLD_PAN", Material.BROWN_TERRACOTTA, "&6Electric Gold Pan", "", LoreBuilder.machine(MachineTier.BASIC, MachineType.MACHINE), LoreBuilder.speed(1), LoreBuilder.powerPerSecond(2)); public static final SlimefunItemStack ELECTRIC_GOLD_PAN_2 = new SlimefunItemStack("ELECTRIC_GOLD_PAN_2", Material.BROWN_TERRACOTTA, "&6Electric Gold Pan &7(&eII&7)", "", LoreBuilder.machine(MachineTier.BASIC, MachineType.MACHINE), LoreBuilder.speed(3), LoreBuilder.powerPerSecond(4)); From 087e9857954352eecc8b449e527cb1c70b2420ad Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 24 Nov 2023 18:49:34 +0100 Subject: [PATCH 30/66] fix auto update --- .../thebusybiscuit/slimefun4/core/services/UpdaterService.java | 2 +- .../slimefun4/core/services/TestUpdaterService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index ebd5f971e6..b556789a91 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -58,7 +58,7 @@ public UpdaterService(@Nonnull Slimefun plugin, @Nonnull String version, @Nonnul if (version.contains("UNOFFICIAL")) { // This Server is using a modified build that is not a public release. branch = SlimefunBranch.UNOFFICIAL; - } else if (version.startsWith("DEV - ")) { + } else if (version.startsWith("Dev - ")) { // If we are using a development build, we want to switch to our custom try { autoUpdater = new BlobBuildUpdater(plugin, file, "Slimefun4", "Dev"); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/core/services/TestUpdaterService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/core/services/TestUpdaterService.java index 9f76c65d42..4de8e01e62 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/core/services/TestUpdaterService.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/core/services/TestUpdaterService.java @@ -33,7 +33,7 @@ public static void unload() { @Test @DisplayName("Test if the development branch is recognized correctly") void testDevelopmentBuilds() { - UpdaterService service = new UpdaterService(plugin, "DEV - 131 (git 123456)", file); + UpdaterService service = new UpdaterService(plugin, "Dev - 131 (git 123456)", file); Assertions.assertEquals(SlimefunBranch.DEVELOPMENT, service.getBranch()); Assertions.assertTrue(service.getBranch().isOfficial()); // Cannot currently be tested... yay From b3e4e5aebbeb5f8da180bdeef74cd860ac7bd6cb Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 5 Dec 2023 22:50:22 +0100 Subject: [PATCH 31/66] e2e test on latest version --- .github/workflows/e2e-testing.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-testing.yml b/.github/workflows/e2e-testing.yml index 1033765601..126a4e6e94 100644 --- a/.github/workflows/e2e-testing.yml +++ b/.github/workflows/e2e-testing.yml @@ -25,7 +25,7 @@ jobs: javaVersion: '18' - mcVersion: '1.19.4' javaVersion: '19' - - mcVersion: '1.20.1' + - mcVersion: 'latest' javaVersion: '20' steps: @@ -48,10 +48,16 @@ jobs: - name: Download ${{ matrix.mcVersion }} Paper run: | VERSION="${{ matrix.mcVersion }}" + if [ "$VERSION" == "latest" ]; then + VERSION=$(curl https://api.papermc.io/v2/projects/paper/ -s | jq -r '.versions[-1]') + fi + BUILD_JAR=$(curl -s "https://api.papermc.io/v2/projects/paper/versions/$VERSION/builds" \ | jq '.builds[-1] | "\(.build) \(.downloads.application.name)"' -r) BUILD=$(echo "$BUILD_JAR" | awk '{print $1}') JAR_FILE=$(echo "$BUILD_JAR" | awk '{print $2}') + + echo "Downloading... https://api.papermc.io/v2/projects/paper/versions/$VERSION/builds/$BUILD/downloads/$JAR_FILE" curl -o paper.jar \ "https://api.papermc.io/v2/projects/paper/versions/$VERSION/builds/$BUILD/downloads/$JAR_FILE" From 6ce0911a1660df4c938260e3fb6c300c471af624 Mon Sep 17 00:00:00 2001 From: Jeroen <48769316+iTwins@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:54:50 +0100 Subject: [PATCH 32/66] Add config option for radiation (#3988) --- .../thebusybiscuit/slimefun4/implementation/Slimefun.java | 6 +++++- src/main/resources/config.yml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java index 2b8dd78f02..e8c7b460a1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java @@ -367,9 +367,13 @@ private void onPluginStart() { // Armor Update Task if (config.getBoolean("options.enable-armor-effects")) { new SlimefunArmorTask().schedule(this, config.getInt("options.armor-update-interval") * 20L); - new RadiationTask().schedule(this, config.getInt("options.radiation-update-interval") * 20L); + if (config.getBoolean("options.enable-radiation")) { + new RadiationTask().schedule(this, config.getInt("options.radiation-update-interval") * 20L); + } new RainbowArmorTask().schedule(this, config.getInt("options.rainbow-armor-update-interval") * 20L); new SolarHelmetTask().schedule(this, config.getInt("options.armor-update-interval")); + } else if (config.getBoolean("options.enable-radiation")) { + logger.log(Level.WARNING, "Cannot enable radiation while armor effects are disabled."); } // Starting our tasks diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c77b5463b8..ece2aa3410 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -8,6 +8,7 @@ options: chat-prefix: '&a&lSlimefun 4&7> ' armor-update-interval: 10 enable-armor-effects: true + enable-radiation: true radiation-update-interval: 1 radiation-grace-period: 15 rainbow-armor-update-interval: 3 From 5f9da5b146e12dde22b6023b859889f7f3340df0 Mon Sep 17 00:00:00 2001 From: Jeroen <48769316+iTwins@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:56:23 +0100 Subject: [PATCH 33/66] Filter unlocked researches of player (#3996) --- .../slimefun4/api/player/PlayerProfile.java | 43 +++++++++++++------ .../slimefun4/api/researches/Research.java | 17 ++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java index 2362a91d4a..7b975f5702 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.api.player; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -36,7 +37,6 @@ import io.github.thebusybiscuit.slimefun4.api.events.AsyncProfileLoadEvent; import io.github.thebusybiscuit.slimefun4.api.gps.Waypoint; import io.github.thebusybiscuit.slimefun4.api.items.HashedArmorpiece; -import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.api.researches.Research; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor; @@ -325,35 +325,52 @@ public final void markDirty() { return Optional.empty(); } - // returns the amount of researches with at least 1 enabled item - private int nonEmptyResearches() { - return (int) Slimefun.getRegistry().getResearches() - .stream() - .filter(research -> research.getAffectedItems().stream().anyMatch(item -> item.getState() == ItemState.ENABLED)) - .count(); + private int countNonEmptyResearches(@Nonnull Collection researches) { + int count = 0; + for (Research research : researches) { + if (research.hasEnabledItems()) { + count++; + } + } + return count; } + /** + * This method gets the research title, as defined in {@code config.yml}, + * of this {@link PlayerProfile} based on the fraction + * of unlocked {@link Research}es of this player. + * + * @return The research title of this {@link PlayerProfile} + */ public @Nonnull String getTitle() { List titles = Slimefun.getRegistry().getResearchRanks(); - float fraction = (float) researches.size() / nonEmptyResearches(); + int allResearches = countNonEmptyResearches(Slimefun.getRegistry().getResearches()); + float fraction = (float) countNonEmptyResearches(researches) / allResearches; int index = (int) (fraction * (titles.size() - 1)); return titles.get(index); } + /** + * This sends the statistics for the specified {@link CommandSender} + * to the {@link CommandSender}. This includes research title, research progress + * and total xp spent. + * + * @param sender The {@link CommandSender} for which to get the statistics and send them to. + */ public void sendStats(@Nonnull CommandSender sender) { - Set unlockedResearches = getResearches(); - int levels = unlockedResearches.stream().mapToInt(Research::getCost).sum(); - int allResearches = nonEmptyResearches(); + int unlockedResearches = countNonEmptyResearches(getResearches()); + int levels = getResearches().stream().mapToInt(Research::getCost).sum(); + int allResearches = countNonEmptyResearches(Slimefun.getRegistry().getResearches()); - float progress = Math.round(((unlockedResearches.size() * 100.0F) / allResearches) * 100.0F) / 100.0F; + float progress = Math.round(((unlockedResearches * 100.0F) / allResearches) * 100.0F) / 100.0F; sender.sendMessage(""); sender.sendMessage(ChatColors.color("&7Statistics for Player: &b" + name)); sender.sendMessage(""); sender.sendMessage(ChatColors.color("&7Title: " + ChatColor.AQUA + getTitle())); - sender.sendMessage(ChatColors.color("&7Research Progress: " + NumberUtils.getColorFromPercentage(progress) + progress + " &r% " + ChatColor.YELLOW + '(' + unlockedResearches.size() + " / " + allResearches + ')')); + sender.sendMessage(ChatColors.color("&7Research Progress: " + NumberUtils.getColorFromPercentage(progress) + progress + " &r% " + ChatColor.YELLOW + '(' + unlockedResearches + " / " + allResearches + ')')); sender.sendMessage(ChatColors.color("&7Total XP Levels spent: " + ChatColor.AQUA + levels)); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java index 6a4ee6b528..c4c96e4e79 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/researches/Research.java @@ -22,6 +22,7 @@ import io.github.thebusybiscuit.slimefun4.api.events.PlayerPreResearchEvent; import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation; @@ -197,6 +198,22 @@ public List getAffectedItems() { return items; } + /** + * This method checks whether there is at least one enabled {@link SlimefunItem} + * included in this {@link Research}. + * + * @return whether there is at least one enabled {@link SlimefunItem} + * included in this {@link Research}. + */ + public boolean hasEnabledItems() { + for (SlimefunItem item : items) { + if (item.getState() == ItemState.ENABLED) { + return true; + } + } + return false; + } + /** * Handle what to do when a {@link Player} clicks on an un-researched item in * a {@link SlimefunGuideImplementation}. From 978b1a4d009e33b5ca670d1e1c95399bb137841d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:28:52 +0100 Subject: [PATCH 34/66] [CI skip] Update actions/setup-java action to v4 --- .github/workflows/discord-webhook.yml | 2 +- .github/workflows/e2e-testing.yml | 2 +- .github/workflows/maven-compiler.yml | 2 +- .github/workflows/pull-request.yml | 2 +- .github/workflows/sonarcloud.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/discord-webhook.yml b/.github/workflows/discord-webhook.yml index a34070de5d..3364a75bb1 100644 --- a/.github/workflows/discord-webhook.yml +++ b/.github/workflows/discord-webhook.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: Set up Java JDK 17 - uses: actions/setup-java@v3.13.0 + uses: actions/setup-java@v4.0.0 with: distribution: 'adopt' java-version: '17' diff --git a/.github/workflows/e2e-testing.yml b/.github/workflows/e2e-testing.yml index 126a4e6e94..d973289d91 100644 --- a/.github/workflows/e2e-testing.yml +++ b/.github/workflows/e2e-testing.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Set up JDK - uses: actions/setup-java@v3.11.0 + uses: actions/setup-java@v4.0.0 with: distribution: temurin java-version: ${{ matrix.javaVersion }} diff --git a/.github/workflows/maven-compiler.yml b/.github/workflows/maven-compiler.yml index 4145068aa7..7529fdbd77 100644 --- a/.github/workflows/maven-compiler.yml +++ b/.github/workflows/maven-compiler.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3.13.0 + uses: actions/setup-java@v4.0.0 with: distribution: 'adopt' java-version: '17' diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b564ba9e26..89b9374dcf 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3.13.0 + uses: actions/setup-java@v4.0.0 with: distribution: 'adopt' java-version: '17' diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 06abb9b2bf..6fe3823205 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -24,7 +24,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 17 - uses: actions/setup-java@v3.13.0 + uses: actions/setup-java@v4.0.0 with: distribution: 'adopt' java-version: '17' From 9c02d9a5eb0f3d10e705e38ba98114d922bd21c1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:29:49 +0100 Subject: [PATCH 35/66] [CI skip] Update actions/checkout action to v4 --- .github/workflows/e2e-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-testing.yml b/.github/workflows/e2e-testing.yml index d973289d91..0f7cc57413 100644 --- a/.github/workflows/e2e-testing.yml +++ b/.github/workflows/e2e-testing.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v4.0.0 From 5f3729a01b115e5bf87c65f5fed67ae63be8adc5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:30:15 +0100 Subject: [PATCH 36/66] [CI skip] Update actions/github-script action to v7 --- .github/workflows/preview-builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview-builds.yml b/.github/workflows/preview-builds.yml index 947ded7153..b9e7b5d26a 100644 --- a/.github/workflows/preview-builds.yml +++ b/.github/workflows/preview-builds.yml @@ -20,7 +20,7 @@ jobs: # Kinda jank way to grab the PR and commit hash and then download the artifact # TODO: Move this code to our own mini-action - name: Grab PR & run ID and download the artifact - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ From e81ed116e1e401e8843e6e68a64cd5ebfa1a4065 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:30:32 +0100 Subject: [PATCH 37/66] [CI skip] Update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.6.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 229c6bddbb..2f00ceea8c 100644 --- a/pom.xml +++ b/pom.xml @@ -239,7 +239,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.0 + 3.6.3 ${project.basedir} From 93829d800a92d4b4509baf89e06e5851a8be6f2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:30:49 +0100 Subject: [PATCH 38/66] [CI skip] Update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.2.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f00ceea8c..59ef2165cc 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.1 + 3.2.2 org.junit.jupiter:junit-jupiter From 007309d29a330244050e30ef3fe7c32d25b95c00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:33:07 +0100 Subject: [PATCH 39/66] [CI skip] Update dependency org.mockito:mockito-core to v5.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59ef2165cc..714b7afedb 100644 --- a/pom.xml +++ b/pom.xml @@ -389,7 +389,7 @@ org.mockito mockito-core - 5.6.0 + 5.8.0 test From 88daec571d124613914fa9b97cb50a37f1fcac5b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:34:40 +0100 Subject: [PATCH 40/66] [CI skip] Update dependency org.slf4j:slf4j-simple to v2.0.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 714b7afedb..6984311b42 100644 --- a/pom.xml +++ b/pom.xml @@ -395,7 +395,7 @@ org.slf4j slf4j-simple - 2.0.7 + 2.0.9 test From d2f7486e082c855a19759e6af863d82ce06ad274 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:07:07 +0100 Subject: [PATCH 41/66] [CI skip] Update dependency org.junit.jupiter:junit-jupiter to v5.10.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6984311b42..fd065cb4a7 100644 --- a/pom.xml +++ b/pom.xml @@ -383,7 +383,7 @@ org.junit.jupiter junit-jupiter - 5.10.0 + 5.10.1 test From 530f375a1f1f24b2344a2f372ec20f221b30957f Mon Sep 17 00:00:00 2001 From: iTwins Date: Wed, 6 Dec 2023 20:27:05 +0100 Subject: [PATCH 42/66] revert unnecessary changes --- .../core/machines/MachineProcessor.java | 33 ++++++++-------- .../implementation/items/geo/GEOMiner.java | 38 ++++++++++--------- .../operations/MiningOperation.java | 9 ++--- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index c1e69b39c8..dd2349e196 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -6,8 +6,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import com.google.common.base.Preconditions; - +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; @@ -47,7 +46,7 @@ public class MachineProcessor { * The owner of this {@link MachineProcessor}. */ public MachineProcessor(@Nonnull MachineProcessHolder owner) { - Preconditions.checkArgument(owner != null, "The MachineProcessHolder cannot be null."); + Validate.notNull(owner, "The MachineProcessHolder cannot be null."); this.owner = owner; } @@ -94,8 +93,8 @@ public void setProgressBar(@Nullable ItemStack progressBar) { * {@link MachineOperation} has already been started at that {@link Location}. */ public boolean startOperation(@Nonnull Location loc, @Nonnull T operation) { - Preconditions.checkArgument(loc != null, "The location must not be null"); - Preconditions.checkArgument(operation != null, "The operation cannot be null"); + Validate.notNull(loc, "The location must not be null"); + Validate.notNull(operation, "The operation cannot be null"); return startOperation(new BlockPosition(loc), operation); } @@ -112,8 +111,8 @@ public boolean startOperation(@Nonnull Location loc, @Nonnull T operation) { * {@link MachineOperation} has already been started at that {@link Block}. */ public boolean startOperation(@Nonnull Block b, @Nonnull T operation) { - Preconditions.checkArgument(b != null, "The Block must not be null"); - Preconditions.checkArgument(operation != null, "The machine operation cannot be null"); + Validate.notNull(b, "The Block must not be null"); + Validate.notNull(operation, "The machine operation cannot be null"); return startOperation(new BlockPosition(b), operation); } @@ -130,8 +129,8 @@ public boolean startOperation(@Nonnull Block b, @Nonnull T operation) { * {@link MachineOperation} has already been started at that {@link BlockPosition}. */ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) { - Preconditions.checkArgument(pos != null, "The BlockPosition must not be null"); - Preconditions.checkArgument(operation != null, "The machine operation cannot be null"); + Validate.notNull(pos, "The BlockPosition must not be null"); + Validate.notNull(operation, "The machine operation cannot be null"); return machines.putIfAbsent(pos, operation) == null; } @@ -145,7 +144,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull Location loc) { - Preconditions.checkArgument(loc != null, "The location cannot be null"); + Validate.notNull(loc, "The location cannot be null"); return getOperation(new BlockPosition(loc)); } @@ -159,7 +158,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull Block b) { - Preconditions.checkArgument(b != null, "The Block cannot be null"); + Validate.notNull(b, "The Block cannot be null"); return getOperation(new BlockPosition(b)); } @@ -173,7 +172,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * @return The current {@link MachineOperation} or null. */ public @Nullable T getOperation(@Nonnull BlockPosition pos) { - Preconditions.checkArgument(pos != null, "The BlockPosition must not be null"); + Validate.notNull(pos, "The BlockPosition must not be null"); return machines.get(pos); } @@ -188,7 +187,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull Location loc) { - Preconditions.checkArgument(loc != null, "The location should not be null"); + Validate.notNull(b, "The Block should not be null"); return endOperation(new BlockPosition(loc)); } @@ -203,7 +202,7 @@ public boolean endOperation(@Nonnull Location loc) { * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull Block b) { - Preconditions.checkArgument(b != null, "The Block should not be null"); + Validate.notNull(b, "The Block should not be null"); return endOperation(new BlockPosition(b)); } @@ -218,7 +217,7 @@ public boolean endOperation(@Nonnull Block b) { * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull BlockPosition pos) { - Preconditions.checkArgument(pos != null, "The BlockPosition cannot be null"); + Validate.notNull(pos, "The BlockPosition cannot be null"); T operation = machines.remove(pos); @@ -241,8 +240,8 @@ public boolean endOperation(@Nonnull BlockPosition pos) { } public void updateProgressBar(@Nonnull BlockMenu inv, int slot, @Nonnull T operation) { - Preconditions.checkArgument(inv != null, "The inventory must not be null."); - Preconditions.checkArgument(operation != null, "The MachineOperation must not be null."); + Validate.notNull(inv, "The inventory must not be null."); + Validate.notNull(operation, "The MachineOperation must not be null."); if (getProgressBar() == null) { // No progress bar, no need to update anything. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index d0f8c21b62..e8bc21fa64 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -7,8 +7,7 @@ import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import com.google.common.base.Preconditions; - +import org.apache.commons.lang.Validate; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -123,7 +122,7 @@ public int getSpeed() { * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ public final @Nonnull GEOMiner setCapacity(int capacity) { - Preconditions.checkArgument(capacity > 0, "The capacity must be greater than zero!"); + Validate.isTrue(capacity > 0, "The capacity must be greater than zero!"); if (getState() == ItemState.UNREGISTERED) { this.energyCapacity = capacity; @@ -141,8 +140,8 @@ public int getSpeed() { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final @Nonnull GEOMiner setProcessingSpeed(int speed) { - Preconditions.checkArgument(speed > 0, "The speed must be greater than zero!"); + public final GEOMiner setProcessingSpeed(int speed) { + Validate.isTrue(speed > 0, "The speed must be greater than zero!"); this.processingSpeed = speed; return this; @@ -156,10 +155,10 @@ public int getSpeed() { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final @Nonnull GEOMiner setEnergyConsumption(int energyConsumption) { - Preconditions.checkArgument(energyConsumption > 0, "The energy consumption must be greater than zero!"); - Preconditions.checkArgument(energyCapacity > 0, "You must specify the capacity before you can set the consumption amount."); - Preconditions.checkArgument(energyConsumption <= energyCapacity, "The energy consumption cannot be higher than the capacity (" + energyCapacity + ')'); + public final GEOMiner setEnergyConsumption(int energyConsumption) { + Validate.isTrue(energyConsumption > 0, "The energy consumption must be greater than zero!"); + Validate.isTrue(energyCapacity > 0, "You must specify the capacity before you can set the consumption amount."); + Validate.isTrue(energyConsumption <= energyCapacity, "The energy consumption cannot be higher than the capacity (" + energyCapacity + ')'); this.energyConsumedPerTick = energyConsumption; return this; @@ -189,17 +188,19 @@ public void register(@Nonnull SlimefunAddon addon) { } } - private @Nonnull BlockPlaceHandler onBlockPlace() { + @Nonnull + private BlockPlaceHandler onBlockPlace() { return new BlockPlaceHandler(false) { @Override - public void onPlayerPlace(@Nonnull BlockPlaceEvent e) { + public void onPlayerPlace( BlockPlaceEvent e) { updateHologram(e.getBlock(), "&7Idling..."); } }; } - private @Nonnull BlockBreakHandler onBlockBreak() { + @Nonnull + private BlockBreakHandler onBlockBreak() { return new SimpleBlockBreakHandler() { @Override @@ -216,18 +217,21 @@ public void onBlockBreak(@Nonnull Block b) { }; } + @Nonnull @Override - public @Nonnull int[] getInputSlots() { + public int[] getInputSlots() { return new int[0]; } + @Nonnull @Override - public @Nonnull int[] getOutputSlots() { + public int[] getOutputSlots() { return OUTPUT_SLOTS; } + @Nonnull @Override - public @Nonnull List getDisplayRecipes() { + public List getDisplayRecipes() { List displayRecipes = new LinkedList<>(); for (GEOResource resource : Slimefun.getRegistry().getGEOResources().values()) { @@ -245,7 +249,7 @@ public void onBlockBreak(@Nonnull Block b) { } @Override - public @Nonnull EnergyNetComponentType getEnergyComponentType() { + public EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.CONSUMER; } @@ -324,7 +328,7 @@ private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { if (resource.isObtainableFromGEOMiner()) { OptionalInt optional = Slimefun.getGPSNetwork().getResourceManager().getSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4); - if (optional.isEmpty()) { + if (!optional.isPresent()) { updateHologram(b, "&4GEO-Scan required!"); return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java index 83480ce63f..e7f94c98bf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/operations/MiningOperation.java @@ -2,8 +2,7 @@ import javax.annotation.Nonnull; -import com.google.common.base.Preconditions; - +import org.apache.commons.lang.Validate; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.core.machines.MachineOperation; @@ -23,8 +22,8 @@ public class MiningOperation implements MachineOperation { private int currentTicks = 0; public MiningOperation(@Nonnull ItemStack result, int totalTicks) { - Preconditions.checkArgument(result != null, "The result cannot be null"); - Preconditions.checkArgument(totalTicks >= 0, "The amount of total ticks must be a positive integer or zero, received: " + totalTicks); + Validate.notNull(result, "The result cannot be null"); + Validate.isTrue(totalTicks >= 0, "The amount of total ticks must be a positive integer or zero, received: " + totalTicks); this.result = result; this.totalTicks = totalTicks; @@ -32,7 +31,7 @@ public MiningOperation(@Nonnull ItemStack result, int totalTicks) { @Override public void addProgress(int num) { - Preconditions.checkArgument(num > 0, "Progress must be positive."); + Validate.isTrue(num > 0, "Progress must be positive."); currentTicks += num; } From c7506d863b9fbd2da8ffbd81f4b1d02c47969425 Mon Sep 17 00:00:00 2001 From: iTwins Date: Wed, 6 Dec 2023 20:30:44 +0100 Subject: [PATCH 43/66] revert these unnecessary changes as well --- .../slimefun4/core/machines/MachineProcessor.java | 2 +- .../slimefun4/implementation/items/geo/GEOMiner.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java index dd2349e196..9ec92b578a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/machines/MachineProcessor.java @@ -187,7 +187,7 @@ public boolean startOperation(@Nonnull BlockPosition pos, @Nonnull T operation) * {@link MachineOperation} to begin with. */ public boolean endOperation(@Nonnull Location loc) { - Validate.notNull(b, "The Block should not be null"); + Validate.notNull(loc, "The location should not be null"); return endOperation(new BlockPosition(loc)); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index e8bc21fa64..cc09eb0ac6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -121,7 +121,7 @@ public int getSpeed() { * * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ - public final @Nonnull GEOMiner setCapacity(int capacity) { + public final GEOMiner setCapacity(int capacity) { Validate.isTrue(capacity > 0, "The capacity must be greater than zero!"); if (getState() == ItemState.UNREGISTERED) { @@ -193,7 +193,7 @@ private BlockPlaceHandler onBlockPlace() { return new BlockPlaceHandler(false) { @Override - public void onPlayerPlace( BlockPlaceEvent e) { + public void onPlayerPlace(BlockPlaceEvent e) { updateHologram(e.getBlock(), "&7Idling..."); } }; From f49d2dda4342c8126465c4983a4cdbc78a5a1dc8 Mon Sep 17 00:00:00 2001 From: J3fftw <44972470+J3fftw1@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:07:50 +0100 Subject: [PATCH 44/66] fix rate limiting issues (#4042) --- pom.xml | 2 +- .../slimefun4/core/services/github/GitHubTask.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fd065cb4a7..8a9143c52f 100644 --- a/pom.xml +++ b/pom.xml @@ -355,7 +355,7 @@ com.github.baked-libs.dough dough-api - 99381b2 + 4b28bd408e compile diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java index 37a45e4315..da42dfa996 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java @@ -133,7 +133,7 @@ private int requestTexture(@Nonnull Contributor contributor, @Nonnull Map uuid = contributor.getUniqueId(); if (!uuid.isPresent()) { - CompletableFuture future = UUIDLookup.forUsername(Slimefun.instance(), contributor.getMinecraftName()); + CompletableFuture future = UUIDLookup.getUuidFromUsername(Slimefun.instance(), contributor.getMinecraftName()); // Fixes #3241 - Do not wait for more than 30 seconds uuid = Optional.ofNullable(future.get(30, TimeUnit.SECONDS)); From e261bbebe9e9e2f6b1cad358698b2e5df2eced46 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:39:49 -0600 Subject: [PATCH 45/66] Strip search term --- .../slimefun4/implementation/guide/SurvivalSlimefunGuide.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java index 2e86296008..b9d0b70b9a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java @@ -350,7 +350,7 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory } ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.search.inventory").replace("%item%", ChatUtils.crop(ChatColor.WHITE, input))); - String searchTerm = input.toLowerCase(Locale.ROOT); + String searchTerm = ChatColor.strip(input.toLowerCase(Locale.ROOT)); if (addToHistory) { profile.getGuideHistory().add(searchTerm); From d0963697ca682f58411ffecbd0ab787834dcf85c Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:48:45 -0600 Subject: [PATCH 46/66] use proper method name --- .../slimefun4/implementation/guide/SurvivalSlimefunGuide.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java index b9d0b70b9a..621431efc1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java @@ -350,7 +350,7 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory } ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "guide.search.inventory").replace("%item%", ChatUtils.crop(ChatColor.WHITE, input))); - String searchTerm = ChatColor.strip(input.toLowerCase(Locale.ROOT)); + String searchTerm = ChatColor.stripColor(input.toLowerCase(Locale.ROOT)); if (addToHistory) { profile.getGuideHistory().add(searchTerm); From e83c9f704e8ca78756aa0ef1bb32457ef0dbc54e Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:48:57 -0600 Subject: [PATCH 47/66] Add test for both normal and colored seearches --- .../core/guide/TestGuideOpening.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java b/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java index 8ed5d838b9..586800bec4 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java @@ -5,11 +5,14 @@ import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; +import io.github.thebusybiscuit.slimefun4.implementation.guide.SurvivalSlimefunGuide; +import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -90,8 +93,28 @@ void testOpenItemStack() throws InterruptedException { } @Test - @DisplayName("Test if the Slimefun Search can be opened from the History") + @DisplayName("Test if the Slimefun Search works with normal and colored querys") void testOpenSearch() throws InterruptedException { + String normalQuery = "iron"; + String coloredQuery = ChatColor.DARK_PURPLE + "iron"; + + SlimefunItem testItem = TestUtilities.mockSlimefunItem(plugin, "IRON_ITEM", new CustomItemStack(Material.IRON_INGOT, "iron item")); + testItem.register(plugin); + + Player player = server.addPlayer(); + PlayerProfile profile = TestUtilities.awaitProfile(player); + SlimefunGuideImplementation guide = new SurvivalSlimefunGuide(false, false); + + guide.openSearch(profile, normalQuery, false); + Assertions.assertTrue(player.getOpenInventory().getTopInventory().contains(testItem.getItem()), "Failed on normal query"); + + guide.openSearch(profile, coloredQuery, false); + Assertions.assertTrue(player.getOpenInventory().getTopInventory().contains(testItem.getItem()), "Failed on colored query"); + } + + @Test + @DisplayName("Test if the Slimefun Search can be opened from the History") + void testOpenSearchHistory() throws InterruptedException { String query = "electric"; SlimefunGuideImplementation guide = Mockito.mock(SlimefunGuideImplementation.class); From 82c8c91451306158a6211d7ce3375fe2cfc566ce Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:28:39 -0600 Subject: [PATCH 48/66] Suggestions and consistency --- .../core/guide/TestGuideOpening.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java b/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java index 586800bec4..a8685b9736 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/core/guide/TestGuideOpening.java @@ -93,10 +93,10 @@ void testOpenItemStack() throws InterruptedException { } @Test - @DisplayName("Test if the Slimefun Search works with normal and colored querys") - void testOpenSearch() throws InterruptedException { - String normalQuery = "iron"; - String coloredQuery = ChatColor.DARK_PURPLE + "iron"; + @DisplayName("Test if the Slimefun Search works with normal and colored terms") + void testOpenSearch_withColoredSearchTerm() throws InterruptedException { + String normalTerm = "iron"; + String coloredTerm = ChatColor.DARK_PURPLE + "iron"; SlimefunItem testItem = TestUtilities.mockSlimefunItem(plugin, "IRON_ITEM", new CustomItemStack(Material.IRON_INGOT, "iron item")); testItem.register(plugin); @@ -105,21 +105,23 @@ void testOpenSearch() throws InterruptedException { PlayerProfile profile = TestUtilities.awaitProfile(player); SlimefunGuideImplementation guide = new SurvivalSlimefunGuide(false, false); - guide.openSearch(profile, normalQuery, false); + guide.openSearch(profile, normalTerm, false); + // Assert we can open with a non-coloured search term Assertions.assertTrue(player.getOpenInventory().getTopInventory().contains(testItem.getItem()), "Failed on normal query"); - guide.openSearch(profile, coloredQuery, false); + guide.openSearch(profile, coloredTerm, false); + // Assert we can open with a coloured search term Assertions.assertTrue(player.getOpenInventory().getTopInventory().contains(testItem.getItem()), "Failed on colored query"); } @Test @DisplayName("Test if the Slimefun Search can be opened from the History") void testOpenSearchHistory() throws InterruptedException { - String query = "electric"; + String term = "electric"; SlimefunGuideImplementation guide = Mockito.mock(SlimefunGuideImplementation.class); - PlayerProfile profile = prepare(guide, history -> history.add(query)); - Mockito.verify(guide).openSearch(profile, query, false); + PlayerProfile profile = prepare(guide, history -> history.add(term)); + Mockito.verify(guide).openSearch(profile, term, false); } @Test From e1d230dcb6c799bc2060f0d70d9d1135f5d37d64 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:47:22 -0600 Subject: [PATCH 49/66] Patch hopefully --- .../slimefun4/implementation/listeners/BlockListener.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 967a6ad6d6..beb241f7ad 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -257,8 +257,9 @@ private void checkForSensitiveBlocks(Block block, Integer count) { if (count >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { return; } + BlockData blockData = block.getBlockData(); + block.setType(Material.AIR, false); for (BlockFace face : CARDINAL_BLOCKFACES) { - block.setType(Material.AIR, false); if (!isSupported(block.getRelative(face).getBlockData(), block.getRelative(face))) { Block relative = block.getRelative(face); for (ItemStack drop : relative.getDrops()) { @@ -267,6 +268,7 @@ private void checkForSensitiveBlocks(Block block, Integer count) { checkForSensitiveBlocks(relative, ++count); } } + block.setBlockData(blockData, false); } /** From d6516613700cdfe928087f7270367e27b895ac84 Mon Sep 17 00:00:00 2001 From: J3fftw <44972470+J3fftw1@users.noreply.github.com> Date: Sat, 9 Dec 2023 04:39:27 +0100 Subject: [PATCH 50/66] 1.19.3 support (#4039) --- .../resources/tags/block_placer_ignored_materials.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/resources/tags/block_placer_ignored_materials.json b/src/main/resources/tags/block_placer_ignored_materials.json index 3f9417b711..bc32030c7f 100644 --- a/src/main/resources/tags/block_placer_ignored_materials.json +++ b/src/main/resources/tags/block_placer_ignored_materials.json @@ -27,7 +27,6 @@ "minecraft:lily_pad", "minecraft:dead_bush", "minecraft:fern", - "minecraft:grass", "minecraft:sea_pickle", "minecraft:nether_wart", "minecraft:seagrass", @@ -35,6 +34,10 @@ "minecraft:kelp", "minecraft:bell", "minecraft:lantern", + { + "id" : "minecraft:grass", + "required" : false + }, { "id" : "minecraft:soul_lantern", "required" : false @@ -58,6 +61,10 @@ { "id" : "minecraft:weeping_vines", "required" : false + }, + { + "id" : "minecraft:short_grass", + "required" : false } ] } From d35798040cc5c3758e897c9d8a3e03c6dd1ab111 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Sat, 9 Dec 2023 09:03:54 -0600 Subject: [PATCH 51/66] Revert Sensitive Block Changes --- .../listeners/BlockListener.java | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index beb241f7ad..f66140c3e7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -14,7 +14,6 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -26,7 +25,6 @@ import org.bukkit.inventory.meta.ItemMeta; import io.github.bakedlibs.dough.protection.Interaction; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.events.ExplosiveToolBreakBlocksEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockBreakEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockPlaceEvent; @@ -171,8 +169,6 @@ public void onBlockBreak(BlockBreakEvent e) { callBlockHandler(e, item, drops, sfItem); - checkForSensitiveBlocks(e.getBlock(), 0); - dropItems(e, drops); } } @@ -243,55 +239,6 @@ private void dropItems(BlockBreakEvent e, List drops) { } } - /** - * This method checks recursively for any sensitive blocks - * that are no longer supported due to this block breaking - * - * @param block - * The {@link Block} in question - * @param count - * The amount of times this has been recursively called - */ - @ParametersAreNonnullByDefault - private void checkForSensitiveBlocks(Block block, Integer count) { - if (count >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { - return; - } - BlockData blockData = block.getBlockData(); - block.setType(Material.AIR, false); - for (BlockFace face : CARDINAL_BLOCKFACES) { - if (!isSupported(block.getRelative(face).getBlockData(), block.getRelative(face))) { - Block relative = block.getRelative(face); - for (ItemStack drop : relative.getDrops()) { - block.getWorld().dropItemNaturally(relative.getLocation(), drop); - } - checkForSensitiveBlocks(relative, ++count); - } - } - block.setBlockData(blockData, false); - } - - /** - * This method checks if the {@link BlockData} would be - * supported at the given {@link Block}. - * - * @param blockData - * The {@link BlockData} to check - * @param block - * The {@link Block} the {@link BlockData} would be at - * @return - * Whether the {@link BlockData} would be supported at the given {@link Block} - */ - @ParametersAreNonnullByDefault - private boolean isSupported(BlockData blockData, Block block) { - if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_19)) { - return blockData.isSupported(block); - } else { - // TODO: Make 1.16-1.18 version. BlockData::isSupported is 1.19+. - return true; - } - } - /** * This method checks for a sensitive {@link Block}. * Sensitive {@link Block Blocks} are pressure plates or saplings, which should be broken From 6a4b1fe794c8857433fae7f1cfa97a88734ee8f3 Mon Sep 17 00:00:00 2001 From: Alessio Colombo Date: Sat, 9 Dec 2023 18:16:34 +0100 Subject: [PATCH 52/66] Fixed vanilla sensitive blocks --- .../listeners/BlockListener.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index f66140c3e7..3526b4d11a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -10,10 +10,13 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -170,6 +173,8 @@ public void onBlockBreak(BlockBreakEvent e) { callBlockHandler(e, item, drops, sfItem); dropItems(e, drops); + + checkForSensitiveBlocks(e.getBlock(), 0, e.isDropItems()); } } @@ -283,6 +288,59 @@ private void checkForSensitiveBlockAbove(Player player, Block block, ItemStack i } } + /** + * This method checks recursively for any sensitive blocks + * that are no longer supported due to this block breaking + * + * @param block + * The {@link Block} in question + * @param count + * The amount of times this has been recursively called + */ + @ParametersAreNonnullByDefault + private void checkForSensitiveBlocks(Block block, Integer count, boolean isDropItems) { + if (count >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { + return; + } + + BlockState state = block.getState(); + block.setType(Material.AIR, false); + for (BlockFace face : CARDINAL_BLOCKFACES) { + if (!isSupported(block.getRelative(face).getBlockData(), block.getRelative(face))) { + Block relative = block.getRelative(face); + if (!isDropItems) { + for (ItemStack drop : relative.getDrops()) { + block.getWorld().dropItemNaturally(relative.getLocation(), drop); + } + } + checkForSensitiveBlocks(relative, ++count, isDropItems); + } + } + block.setBlockData(state.getBlockData(), false); + state.update(true, false); + } + + /** + * This method checks if the {@link BlockData} would be + * supported at the given {@link Block}. + * + * @param blockData + * The {@link BlockData} to check + * @param block + * The {@link Block} the {@link BlockData} would be at + * @return + * Whether the {@link BlockData} would be supported at the given {@link Block} + */ + @ParametersAreNonnullByDefault + private boolean isSupported(BlockData blockData, Block block) { + if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_19)) { + return blockData.isSupported(block); + } else { + // TODO: Make 1.16-1.18 version. BlockData::isSupported is 1.19+. + return true; + } + } + private int getBonusDropsWithFortune(@Nullable ItemStack item, @Nonnull Block b) { int amount = 1; From b3ff154f241612abd973d7affd33105ec41874e7 Mon Sep 17 00:00:00 2001 From: Alessio Colombo Date: Sat, 9 Dec 2023 18:30:56 +0100 Subject: [PATCH 53/66] Import order, comments --- .../slimefun4/implementation/listeners/BlockListener.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 3526b4d11a..60b44b3ab0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -10,7 +10,6 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; @@ -32,6 +31,7 @@ import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockBreakEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockPlaceEvent; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; @@ -168,12 +168,15 @@ public void onBlockBreak(BlockBreakEvent e) { } if (!e.isCancelled()) { + // Checks for Slimefun sensitive blocks above, using Slimefun Tags + // TODO: merge this with the vanilla sensitive block check (when 1.18- is dropped) checkForSensitiveBlockAbove(e.getPlayer(), e.getBlock(), item); callBlockHandler(e, item, drops, sfItem); dropItems(e, drops); + // Checks for vanilla sensitive blocks everywhere checkForSensitiveBlocks(e.getBlock(), 0, e.isDropItems()); } } From b38743d713a8d972d0c996c2545166940e49fa5d Mon Sep 17 00:00:00 2001 From: Alessio Colombo Date: Sat, 9 Dec 2023 18:36:00 +0100 Subject: [PATCH 54/66] More comments --- .../slimefun4/implementation/listeners/BlockListener.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 60b44b3ab0..91fb9eb291 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -307,6 +307,7 @@ private void checkForSensitiveBlocks(Block block, Integer count, boolean isDropI } BlockState state = block.getState(); + // We set the block to air to make use of BlockData#isSupported. block.setType(Material.AIR, false); for (BlockFace face : CARDINAL_BLOCKFACES) { if (!isSupported(block.getRelative(face).getBlockData(), block.getRelative(face))) { @@ -319,6 +320,7 @@ private void checkForSensitiveBlocks(Block block, Integer count, boolean isDropI checkForSensitiveBlocks(relative, ++count, isDropItems); } } + // Set the BlockData back: this makes it so containers and spawners drop correctly. This is a hacky fix. block.setBlockData(state.getBlockData(), false); state.update(true, false); } From dd698083553adea13818e3cef13d2b9f33e20233 Mon Sep 17 00:00:00 2001 From: ybw0014 Date: Mon, 11 Dec 2023 16:55:26 -0500 Subject: [PATCH 55/66] chore: update download link (#4053) Co-authored-by: Daniel Walsh Co-authored-by: J3fftw <44972470+J3fftw1@users.noreply.github.com> --- README.md | 2 +- .../slimefun4/api/events/BlockPlacerPlaceEvent.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 72a597409f..46ce4c0096 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Here is a full summary of the differences between the two different versions of | **Bug Reports** | :heavy_check_mark: | :x: | | **testing before release** | :x: | :heavy_check_mark: | | **change logs** | :x: | :memo: **[change log](https://github.com/Slimefun/Slimefun4/blob/master/CHANGELOG.md)** | -| **Download link** | :floppy_disk: **[download latest](https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/master/)** | :floppy_disk: **[download "stable"](https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/)** | +| **Download links** | :floppy_disk: **[download latest](https://blob.build/project/Slimefun4/Dev)** | :floppy_disk: **[download "stable"](https://blob.build/project/Slimefun4/RC)** | **:exclamation: We wholeheartedly recommend you to use _development builds_, they are the most recent version of Slimefun and also receive the most frequent updates! In fact, "stable" builds are so outdated that we won't accept bug reports from them at all.**
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/BlockPlacerPlaceEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/BlockPlacerPlaceEvent.java index 4ea35c510d..59ee4bdf51 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/BlockPlacerPlaceEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/BlockPlacerPlaceEvent.java @@ -116,4 +116,4 @@ public HandlerList getHandlers() { return getHandlerList(); } -} \ No newline at end of file +} From 88ac05ff31a58624e49a60660ab7b9a719314b82 Mon Sep 17 00:00:00 2001 From: JustAHuman-xD <65748158+JustAHuman-xD@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:51:14 -0600 Subject: [PATCH 56/66] Add TalismanActivateEvent (Updated version of #3920) (#4045) Co-authored-by: TheBusyBiscuit Co-authored-by: Sefiraat Co-authored-by: J3fftw <44972470+J3fftw1@users.noreply.github.com> Co-authored-by: Daniel Walsh Co-authored-by: China Worldstar <40675557+Sniperkaos@users.noreply.github.com> Co-authored-by: cworldstar Co-authored-by: Alessio Colombo <37039432+Sfiguz7@users.noreply.github.com> --- .../api/events/TalismanActivateEvent.java | 96 ++++++++++ .../items/magical/talismans/Talisman.java | 29 ++- .../api/events/TestTalismanActivateEvent.java | 177 ++++++++++++++++++ 3 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/api/events/TalismanActivateEvent.java create mode 100644 src/test/java/io/github/thebusybiscuit/slimefun4/api/events/TestTalismanActivateEvent.java diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/TalismanActivateEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/TalismanActivateEvent.java new file mode 100644 index 0000000000..775691a0a6 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/TalismanActivateEvent.java @@ -0,0 +1,96 @@ +package io.github.thebusybiscuit.slimefun4.api.events; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.Talisman; + +/** + * This {@link PlayerEvent} is called when a {@link Player} activates a {@link Talisman} + * + * @author cworldstar + */ +public class TalismanActivateEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private final Talisman talisman; + private final ItemStack talismanItemStack; + private boolean preventConsumption = false; + private boolean cancelled = false; + + /** + * @param player + * The {@link Player} who activated the talisman. + * + * @param talisman + * The {@link Talisman} that was activated. + * + * @param talismanItem + * The {@link ItemStack} corresponding to the Talisman. + */ + @ParametersAreNonnullByDefault + public TalismanActivateEvent(Player player, Talisman talisman, ItemStack talismanItem) { + super(player); + this.talisman = talisman; + this.talismanItemStack = talismanItem; + } + + /** + * @return The {@link Talisman} used. + */ + public @Nonnull Talisman getTalisman() { + return this.talisman; + } + + /** + * @return The {@link ItemStack} of the used {@link Talisman}. + */ + public @Nonnull ItemStack getTalismanItem() { + return this.talismanItemStack; + } + + /** + * Only applies if {@link Talisman#isConsumable()} is true. + * Defaults to false. + * + * @return Whether the {@link ItemStack} should not be consumed. + */ + public boolean preventsConsumption() { + return this.preventConsumption; + } + + /** + * Only applies if {@link Talisman#isConsumable()} is true. + * + * @param preventConsumption + * Whether the {@link ItemStack} should not be consumed. + */ + public void setPreventConsumption(boolean preventConsumption) { + this.preventConsumption = preventConsumption; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public @Nonnull HandlerList getHandlers() { + return getHandlerList(); + } + + public static @Nonnull HandlerList getHandlerList() { + return handlers; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java index 7ecc6f6fab..9f97f19892 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java @@ -10,6 +10,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; @@ -27,6 +28,7 @@ import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.bakedlibs.dough.items.ItemUtils; +import io.github.thebusybiscuit.slimefun4.api.events.TalismanActivateEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; @@ -186,11 +188,15 @@ public static boolean trigger(Event e, SlimefunItem item, boolean sendMessage) { return false; } } else { - ItemStack enderTalisman = talisman.getEnderVariant(); + SlimefunItemStack enderTalismanItem = talisman.getEnderVariant(); + if (enderTalismanItem == null) { + return false; + } - if (SlimefunUtils.containsSimilarItem(p.getEnderChest(), enderTalisman, true)) { + EnderTalisman enderTalisman = enderTalismanItem.getItem(EnderTalisman.class); + if (enderTalisman != null && SlimefunUtils.containsSimilarItem(p.getEnderChest(), enderTalismanItem, true)) { if (talisman.canUse(p, true)) { - activateTalisman(e, p, p.getEnderChest(), talisman, enderTalisman, sendMessage); + activateTalisman(e, p, p.getEnderChest(), enderTalisman, enderTalismanItem, sendMessage); return true; } else { return false; @@ -203,12 +209,19 @@ public static boolean trigger(Event e, SlimefunItem item, boolean sendMessage) { @ParametersAreNonnullByDefault private static void activateTalisman(Event e, Player p, Inventory inv, Talisman talisman, ItemStack talismanItem, boolean sendMessage) { - consumeItem(inv, talisman, talismanItem); - applyTalismanEffects(p, talisman); - cancelEvent(e, talisman); + TalismanActivateEvent talismanEvent = new TalismanActivateEvent(p, talisman, talismanItem); + Bukkit.getPluginManager().callEvent(talismanEvent); + if (!talismanEvent.isCancelled()) { + if (!talismanEvent.preventsConsumption()) { + consumeItem(inv, talisman, talismanItem); + } - if (sendMessage) { - talisman.sendMessage(p); + applyTalismanEffects(p, talisman); + cancelEvent(e, talisman); + + if (sendMessage) { + talisman.sendMessage(p); + } } } diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/api/events/TestTalismanActivateEvent.java b/src/test/java/io/github/thebusybiscuit/slimefun4/api/events/TestTalismanActivateEvent.java new file mode 100644 index 0000000000..83b49fa231 --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/api/events/TestTalismanActivateEvent.java @@ -0,0 +1,177 @@ +package io.github.thebusybiscuit.slimefun4.api.events; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.Talisman; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerItemBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TestTalismanActivateEvent { + + private static ServerMock server; + private static Slimefun plugin; + private static Player player; + private static SlimefunItem talisman; + private static SlimefunItem enderTalisman; + + @BeforeAll + public static void load() { + server = MockBukkit.mock(); + plugin = MockBukkit.load(Slimefun.class); + + new TalismanListener(plugin); + + talisman = new Talisman(SlimefunItems.TALISMAN_ANVIL, new ItemStack[] {}, true, false, "anvil"); + talisman.register(plugin); + + enderTalisman = SlimefunItem.getById("ENDER_" + talisman.getId()); + + player = server.addPlayer(); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + void activateAnvilTalisman(boolean enderVariant, boolean inEnderChest) { + player.getInventory().clear(); + player.getEnderChest().clear(); + + ItemStack talismanItem = enderVariant ? enderTalisman.getItem() : talisman.getItem(); + ItemStack breakableItem = new ItemStack(Material.IRON_PICKAXE); + + if (inEnderChest) { + player.getEnderChest().addItem(talismanItem); + } else { + player.getInventory().addItem(talismanItem); + } + + player.getInventory().setItemInMainHand(breakableItem); + + PlayerItemBreakEvent event = new PlayerItemBreakEvent(player, breakableItem); + server.getPluginManager().callEvent(event); + } + + @Test + @DisplayName("Test that TalismanActivateEvent is fired when an anvil talisman activates") + void testEventIsFired() { + // Assert the talisman activates in the inventory + activateAnvilTalisman(false, false); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, ignored -> true); + server.getPluginManager().clearEvents(); + + // Assert the talisman activates in the ender chest + activateAnvilTalisman(true, true); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, ignored -> true); + server.getPluginManager().clearEvents(); + // Assert the normal talisman does not activate in the ender chest + activateAnvilTalisman(false, true); + try { + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, ignored -> true); + } catch (AssertionError ignored) { + return; // This is expected; the event should not have fired + } + server.getPluginManager().clearEvents(); + + // Assert the ender talisman does not activate in the inventory + try { + activateAnvilTalisman(true, false); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, ignored -> true); + } catch (AssertionError ignored) { + return; // This is expected; the event should not have fired + } + server.getPluginManager().clearEvents(); + } + + @Test + @DisplayName("Test that the TalismanActivateEvent has the correct fields") + void testEventFields() { + // Assert the talisman activates in the inventory + activateAnvilTalisman(false, false); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertEquals(talisman, event.getTalisman()); + Assertions.assertEquals(talisman.getItem(), event.getTalismanItem()); + Assertions.assertEquals(player, event.getPlayer()); + return true; + }); + server.getPluginManager().clearEvents(); + + // Assert the talisman activates in the ender chest + activateAnvilTalisman(true, true); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertEquals(enderTalisman, event.getTalisman()); + Assertions.assertEquals(enderTalisman.getItem(), event.getTalismanItem()); + Assertions.assertEquals(player, event.getPlayer()); + return true; + }); + server.getPluginManager().clearEvents(); + } + + @Test + @DisplayName("Test that the TalismanActivateEvent can be cancelled") + void testEventCanBeCancelled() { + server.getPluginManager().registerEvents(new Listener() { + @EventHandler + public void onTalismanActivate(TalismanActivateEvent event) { + event.setCancelled(true); + } + }, plugin); + + // Assert the talisman activates in the inventory + activateAnvilTalisman(false, false); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertTrue(event.isCancelled()); + return true; + }); + server.getPluginManager().clearEvents(); + + // Assert the talisman activates in the ender chest + activateAnvilTalisman(true, true); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertTrue(event.isCancelled()); + return true; + }); + server.getPluginManager().clearEvents(); + } + + @Test + @DisplayName("Test that the TalismanActivateEvent can prevent consumption") + void testEventCanPreventConsumption() { + server.getPluginManager().registerEvents(new Listener() { + @EventHandler + public void onTalismanActivate(TalismanActivateEvent event) { + event.setPreventConsumption(true); + } + }, plugin); + + // Assert the talisman activates in the inventory + activateAnvilTalisman(false, false); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertTrue(event.preventsConsumption()); + return true; + }); + server.getPluginManager().clearEvents(); + + // Assert the talisman activates in the ender chest + activateAnvilTalisman(true, true); + server.getPluginManager().assertEventFired(TalismanActivateEvent.class, event -> { + Assertions.assertTrue(event.preventsConsumption()); + return true; + }); + server.getPluginManager().clearEvents(); + } +} From 9deb0fa6d535703aa3f2a83c3baba2d5ed5bf8d9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:49:21 +0100 Subject: [PATCH 57/66] [CI skip] chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.2.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8a9143c52f..8dc81f23f4 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.2 + 3.2.3 org.junit.jupiter:junit-jupiter From 09d6f64a60c749e0d52ec67863dc368483796af0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:49:34 +0100 Subject: [PATCH 58/66] [CI skip] fix(deps): update dependency com.sk89q.worldedit:worldedit-core to v7.2.18 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8dc81f23f4..92f579ec10 100644 --- a/pom.xml +++ b/pom.xml @@ -418,7 +418,7 @@ com.sk89q.worldedit worldedit-core - 7.2.17 + 7.2.18 provided From bbfb9734b9f549d7e82291eff041f9b666a61b63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:49:50 +0100 Subject: [PATCH 59/66] [CI skip] fix(deps): update dependency com.sk89q.worldedit:worldedit-bukkit to v7.2.18 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 92f579ec10..6a45ac2cf0 100644 --- a/pom.xml +++ b/pom.xml @@ -432,7 +432,7 @@ com.sk89q.worldedit worldedit-bukkit - 7.2.17 + 7.2.18 provided From 18565eba2e5ce5031a6b48fc5b9a3bb511ea9a94 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:21:01 +0100 Subject: [PATCH 60/66] [CI skip] Update dependency com.gmail.nossr50.mcMMO:mcMMO to v2.1.226 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a45ac2cf0..3dbac1e08e 100644 --- a/pom.xml +++ b/pom.xml @@ -446,7 +446,7 @@ com.gmail.nossr50.mcMMO mcMMO - 2.1.225 + 2.1.226 provided From 9e8758b990809bc94243e5d9fed7bdc19aaac3d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:24:53 +0100 Subject: [PATCH 61/66] [CI skip] Update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dbac1e08e..a548bfc1a6 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.12.0 From a56aacd4d62878a718f1b83076344d79cfd576bb Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Tue, 19 Dec 2023 20:35:26 +0000 Subject: [PATCH 62/66] Update changelog for RC-36 (#4066) --- CHANGELOG.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd01fbb11..fd52f7a3dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Table of contents -- [Release Candidate 36 (TBD)](#release-candidate-36-tbd) -- [Release Candidate 35 (7 Jul 2023)](#release-candidate-35-7-jul-2023) +- [Release Candidate 37 (TBD)](#release-candidate-37-tbd) +- [Release Candidate 36 (20 Dec 2023)](#release-candidate-36-20-dec-2023) +- [Release Candidate 35 (07 Jul 2023)](#release-candidate-35-07-jul-2023) - [Release Candidate 34 (20 Jun 2023)](#release-candidate-34-20-jun-2023) - [Release Candidate 33 (07 Jan 2023)](#release-candidate-33-07-jan-2023) - [Release Candidate 32 (26 Jun 2022)](#release-candidate-32-26-jun-2022) @@ -36,15 +37,55 @@ - [Release Candidate 2 (29 Sep 2019)](#release-candidate-2-29-sep-2019) - [Release Candidate 1 (26 Sep 2019)](#release-candidate-1-26-sep-2019) -## Release Candidate 36 (TBD) +## Release Candidate 37 (TBD) + +## Release Candidate 36 (20 Dec 2023) #### Additions +* Added e2e testing to PRs to better ensure compatibility +* Added compatibility to 1.20+ +* Added rainbow armor +* Added grace periods to radiation +* Added cherry log to android woodcutter +* Added blackstone recipes to Grindstone and Ore Crusher (#3912) +* Added Enchanted Golden Apple recipe (suggestion #2147 from punished_Garett) (#3591) +* Added new flags for timings (#3246) +* Added yaw to GPS Waypoints +* (API) Add MultiBlockCraftEvent (#3928) +* (API) Add TalismanActivateEvent (#4045) #### Changes +* Changed the radiation system +* Removed backwards compatibility +* (API) Improve performance for clearAllBlockInfoAtChunk +* Change Energized GPS Transmitter values to follow the pattern of previous tiers (#3915) +* Allowed the sword of beheading to drop piglin heads +* Improvements to BlockStorage handling (#3911) +* Moved builds to https://blob.build #### Fixes - -## Release Candidate 35 (7 Jul 2023) +* Fix #3444 +* Fix #3507 +* Fix possible enchantment duplication +* Fix Different Time of Pan Recipes +* Fix some of the reported blocks not working (#3848) +* Fix Soulbound Runes not working (#3932) +* Fix #3836 +* Fix unable to craft soulbound backpack with woven backpack with id (#3939) +* Fix getting radiated when not supposed to +* Fix geo miner voiding resources +* Fix sensitive blocks attached to sf blocks not dropping (1.19+) +* Fix breaking sf block with not unlocked item duping contents (#3976) +* Fix the case of SlimefunItem#itemhandlers +* Fix taking damage on head collision while wearing elytra cap (#3760) +* Fix heads showing as steve (#4027) +* Fix grappling hook not working due to bat dying (#3926) +* Fix freezer material +* Fix auto update +* Fix rate limiting issues (#4042) +* Fix orebfuscator plugin with blocks when gold panning (#3921) + +## Release Candidate 35 (07 Jul 2023) #### Additions * Added `sounds.yml` file to configure sound effects for Slimefun From 6b038509655817fa9d6ff80cd54dd5c5fade6438 Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Wed, 27 Dec 2023 02:59:19 +0000 Subject: [PATCH 63/66] Fix contributor head being pullable (#4072) --- pom.xml | 8 +++++++- .../slimefun4/core/services/github/GitHubTask.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a548bfc1a6..8fa8a7be1d 100644 --- a/pom.xml +++ b/pom.xml @@ -355,7 +355,7 @@ com.github.baked-libs.dough dough-api - 4b28bd408e + da42c2f268 compile @@ -513,6 +513,12 @@ + + com.mojang + authlib + 1.5.25 + provided + commons-lang diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java index da42dfa996..8b065e3ff4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java @@ -142,7 +142,7 @@ private int requestTexture(@Nonnull Contributor contributor, @Nonnull Map future = PlayerSkin.fromPlayerUUID(Slimefun.instance(), uuid.get()); - Optional skin = Optional.of(future.get().toString()); + Optional skin = Optional.of(future.get().getProfile().getBase64Texture()); skins.put(contributor.getMinecraftName(), skin.orElse("")); return skin.orElse(null); } else { From dad6020680bda143fb998020c33aa2270186b0f5 Mon Sep 17 00:00:00 2001 From: Silent <46107752+TheSilentPro@users.noreply.github.com> Date: Sun, 31 Dec 2023 09:58:28 +0100 Subject: [PATCH 64/66] Feature/block drop creative (#3934) Co-authored-by: J3fftw <44972470+J3fftw1@users.noreply.github.com> Co-authored-by: Silent --- .../slimefun4/implementation/listeners/BlockListener.java | 5 ++++- src/main/resources/config.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 91fb9eb291..1db923bebc 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -11,6 +11,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -240,7 +241,9 @@ private void dropItems(BlockBreakEvent e, List drops) { for (ItemStack drop : drops) { // Prevent null or air from being dropped if (drop != null && drop.getType() != Material.AIR) { - e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), drop); + if (e.getPlayer().getGameMode() != GameMode.CREATIVE || Slimefun.getCfg().getBoolean("options.drop-block-creative")) { + e.getBlock().getWorld().dropItemNaturally(e.getBlock().getLocation(), drop); + } } } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ece2aa3410..cb133170e4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -22,6 +22,7 @@ options: burn-players-when-radioactive: true drop-excess-sf-give-items: false backup-data: true + drop-block-creative: true guide: show-vanilla-recipes: true From 4d710fa0b119a400478a22494c0f306648326bd0 Mon Sep 17 00:00:00 2001 From: Daniel Walsh Date: Tue, 2 Jan 2024 12:11:04 +0000 Subject: [PATCH 65/66] Storage rewrite - Phase 1 (#4065) * Phase 1 - wip * Add some tests * wip * Save waypoints * Implement backpacks * Add tests for waypoints * Changes to ADR * Small changes * Fix englandish and some small changes to UUID in PlayerProfile * Fix saving of player data in a few cases * Documentation around deprecated * Add some more tests * Some small doc updates * Make old Waypoint constructor deprecated and fix javadocs --- .adr-dir | 1 + .gitignore | 1 + docs/adr/0001-storage-layer.md | 129 ++++++ docs/adr/README.md | 11 + .../slimefun4/api/gps/GPSNetwork.java | 2 +- .../slimefun4/api/gps/Waypoint.java | 56 ++- .../slimefun4/api/items/SlimefunItem.java | 8 +- .../slimefun4/api/player/PlayerBackpack.java | 71 +++- .../slimefun4/api/player/PlayerProfile.java | 124 ++---- .../slimefun4/implementation/Slimefun.java | 16 + .../slimefun4/storage/Storage.java | 26 ++ .../storage/backend/legacy/LegacyStorage.java | 127 ++++++ .../slimefun4/storage/data/PlayerData.java | 96 +++++ .../slimefun4/api/gps/TestWaypoints.java | 31 +- .../api/profiles/TestPlayerBackpacks.java | 38 +- .../listeners/TestBackpackListener.java | 13 +- .../storage/backend/TestLegacyBackend.java | 383 ++++++++++++++++++ .../slimefun4/test/mocks/MockProfile.java | 9 +- 18 files changed, 975 insertions(+), 167 deletions(-) create mode 100644 .adr-dir create mode 100644 docs/adr/0001-storage-layer.md create mode 100644 docs/adr/README.md create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/storage/Storage.java create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/storage/backend/legacy/LegacyStorage.java create mode 100644 src/main/java/io/github/thebusybiscuit/slimefun4/storage/data/PlayerData.java create mode 100644 src/test/java/io/github/thebusybiscuit/slimefun4/storage/backend/TestLegacyBackend.java diff --git a/.adr-dir b/.adr-dir new file mode 100644 index 0000000000..c73b64aed2 --- /dev/null +++ b/.adr-dir @@ -0,0 +1 @@ +docs/adr diff --git a/.gitignore b/.gitignore index fda2f4a052..f025c1e196 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /.settings/ /.idea/ /.vscode/ +/data-store/ dependency-reduced-pom.xml diff --git a/docs/adr/0001-storage-layer.md b/docs/adr/0001-storage-layer.md new file mode 100644 index 0000000000..0809ed04a2 --- /dev/null +++ b/docs/adr/0001-storage-layer.md @@ -0,0 +1,129 @@ +# 1. Storage layer + +Date: 2023-11-15 +Last update: 2023-12-27 + +**DO NOT rely on any APIs introduced until we finish the work completely!** + +## Status + +Work in progress + +## Context + +Slimefun has been around for a very long time and due to that, the way we +wrote persistence of data has also been around for a very long time. +While Slimefun has grown, the storage layer has never been adapted. +This means that even all these years later, it's using the same old saving/loading. +This isn't necessarily always bad, however, as Slimefun has grown both in terms of content +and the servers using it - we've seen some issues. + +Today, files are saved as YAML files (sometimes with just a JSON object per line), +which is good for a config format but not good for a data store. It can create very large files +that can get corrupted, the way we've been saving data often means loading it all at once as well +rather than lazy-loading and generally isn't very performant. + +For a long time we've been talking about rewriting our data storage in multiple forms +(you may have seen this referenced for "BlockStorage rewrite" or "SQL for PlayerProfiles", etc.). +Now is the time we start to do this, this will be a very large change and will not be done quickly or rushed. + +This ADR talks about the future of our data persistence. + +## Decision + +We want to create a new storage layer abstraction and implementations +which will be backwards-compatible but open up new ways of storing data +within Slimefun. The end end goal is we can quickly and easily support +new storage backends (such as binary storage, SQL, etc.) for things like +[PlayerProfile](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java), [BlockStorage](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java), etc. + +We also want to be generally more efficient in the way we save and load data. +Today, we load way more than is required. +We can improve memory usage by only loading what we need, when we need it. + +We will do this incrementally and at first, in an experimental context. +In that regard, we should aim to minimise the blast radius and lift as much +as possible. + +### Quick changes overview + +* New abstraction over storage to easily support multiple backends. +* Work towards moving away from the legacy YAML based storage. +* Lazy load and save data to more efficiently handle the data life cycle. + +### Implementation details + +There is a new interface called [`Storage`](TBD) which is what all storage +backends will implement. +This will have methods for loading and saving things like +[`PlayerProfile`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java) and [`BlockStorage`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java). + +Then, backends will implement these +(e.g. [`LegacyStorageBackend`](TBD) (today's YAML situation)) +in order to support these functions. +Not all storage backends are required support each data type. +e.g. SQL may not support [`BlockStorage`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java). + + +## Addons + +The goal is that Addons will be able to use and implement new storage backends +if they wish and also be extended so they can load/save things as they wish. + +The first few iterations will not focus on Addon support. We want to ensure +this new storage layer will work and supports what we need it to today. + +This ADR will be updated when we get to supporting Addons properly. + +## Considerations + +This will be a big change therefore we will be doing it as incrementally as +possible. +Changes will be tested while in the PR stage and merged into the Dev releases when possible. +We may do an experimental release if required. + +Phases do not (and very likely will not) be done within a single PR. They will also not have any timeframe attached to them. + +The current plan looks like this: + +* Phase 1 - Implement legacy data backend for [`PlayerProfile`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java). + * We want to load player data using the new storage layer with the current + data system. + * We'll want to monitor for any possible issues and generally refine + how this system should look +* Phase 2 - Implement new experimental binary backend for [`PlayerProfile`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java). + * Create a new backend for binary storage + * Implement in an experimental capacity and allow users to opt-in + * Provide a warning that this is **experimental** and there will be bugs. + * Implement new metric for storage backend being used +* Phase 3 - Mark the new backend as stable for [`PlayerProfile`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java). + * Mark it as stable and remove the warnings once we're sure things are + working correctly + * Create a migration path for users currently using "legacy". + * Enable by default for new servers +* Phase 4 - Move [`BlockStorage`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java) to new storage layer. + * The big one! We're gonna tackle adding this to BlockStorage. + This will probably be a large change and we'll want to be as + careful as possible here. + * Implement `legacy` and `binary` as experimental storage backends + for BlockStorage and allow users to opt-in + * Provide a warning that this is **experimental** and there will be bugs. +* Phase 5 - Mark the new storage layer as stable for [`BlockStorage`](https://github.com/Slimefun/Slimefun4/blob/bbfb9734b9f549d7e82291eff041f9b666a61b63/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java). + * Mark it as stable and remove the warnings once we're sure things are + working correctly + * Ensure migration path works here too. + * Enable by default for new servers +* Phase 6 - Finish up and move anything else we want over + * Move over any other data stores we have to the new layer + * We should probably still do experimental -> stable but it should have + less of a lead time. + +## State of work + +* Phase 1: In progress + * https://github.com/Slimefun/Slimefun4/pull/4065 +* Phase 2: Not started +* Phase 3: Not started +* Phase 4: Not started +* Phase 5: Not started +* Phase 6: Not started diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000000..1762af11cd --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,11 @@ +# ADR + +An ADR (Architecture Decision Record) is a document describing large changes, why we made them, etc. + +## Making a new ADR + +If you're making a large change to Slimefun, we recommend creating an ADR +in order to document why this is being made and how it works for future contributors. + +Please follow the general format of the former ADRs or use a tool +such as [`adr-tools`](https://github.com/npryce/adr-tools) to generate a new document. \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java index d2d9a3c1ee..47574fb2ca 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java @@ -331,7 +331,7 @@ public void addWaypoint(@Nonnull Player p, @Nonnull String name, @Nonnull Locati } } - profile.addWaypoint(new Waypoint(profile, id, event.getLocation(), event.getName())); + profile.addWaypoint(new Waypoint(p.getUniqueId(), id, event.getLocation(), event.getName())); SoundEffect.GPS_NETWORK_ADD_WAYPOINT.playFor(p); Slimefun.getLocalization().sendMessage(p, "gps.waypoint.added", true); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/Waypoint.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/Waypoint.java index 13c8b42561..468b70af6b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/Waypoint.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/Waypoint.java @@ -1,11 +1,13 @@ package io.github.thebusybiscuit.slimefun4.api.gps; import java.util.Objects; +import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World.Environment; import org.bukkit.entity.Player; @@ -30,14 +32,14 @@ */ public class Waypoint { - private final PlayerProfile profile; + private final UUID ownerId; private final String id; private final String name; private final Location location; /** * This constructs a new {@link Waypoint} object. - * + * * @param profile * The owning {@link PlayerProfile} * @param id @@ -46,28 +48,62 @@ public class Waypoint { * The {@link Location} of the {@link Waypoint} * @param name * The name of this {@link Waypoint} + * + * @deprecated Use {@link #Waypoint(UUID, String, Location, String)} instead */ + @Deprecated @ParametersAreNonnullByDefault public Waypoint(PlayerProfile profile, String id, Location loc, String name) { - Validate.notNull(profile, "Profile must never be null!"); + this(profile.getUUID(), id, loc, name); + } + + /** + * This constructs a new {@link Waypoint} object. + * + * @param ownerId + * The owning {@link Player}'s {@link UUID} + * @param id + * The unique id for this {@link Waypoint} + * @param loc + * The {@link Location} of the {@link Waypoint} + * @param name + * The name of this {@link Waypoint} + */ + @ParametersAreNonnullByDefault + public Waypoint(UUID ownerId, String id, Location loc, String name) { + Validate.notNull(ownerId, "owner ID must never be null!"); Validate.notNull(id, "id must never be null!"); Validate.notNull(loc, "Location must never be null!"); Validate.notNull(name, "Name must never be null!"); - this.profile = profile; + this.ownerId = ownerId; this.id = id; this.location = loc; this.name = name; } /** - * This returns the owner of the {@link Waypoint}. + * This returns the owner's {@link UUID} of the {@link Waypoint}. * + * @return The corresponding owner's {@link UUID} + */ + @Nonnull + public UUID getOwnerId() { + return this.ownerId; + } + + /** + * This returns the owner of the {@link Waypoint}. + * * @return The corresponding {@link PlayerProfile} + * + * @deprecated Use {@link #getOwnerId()} instead */ @Nonnull + @Deprecated public PlayerProfile getOwner() { - return profile; + // This is jank and should never actually return null + return PlayerProfile.find(Bukkit.getOfflinePlayer(ownerId)).orElse(null); } /** @@ -126,7 +162,7 @@ public ItemStack getIcon() { */ @Override public int hashCode() { - return Objects.hash(profile.getUUID(), id, name, location); + return Objects.hash(this.ownerId, this.id, this.name, this.location); } /** @@ -139,7 +175,9 @@ public boolean equals(Object obj) { } Waypoint waypoint = (Waypoint) obj; - return profile.getUUID().equals(waypoint.getOwner().getUUID()) && id.equals(waypoint.getId()) && location.equals(waypoint.getLocation()) && name.equals(waypoint.getName()); + return this.ownerId.equals(waypoint.getOwnerId()) + && id.equals(waypoint.getId()) + && location.equals(waypoint.getLocation()) + && name.equals(waypoint.getName()); } - } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java index 20a12ef6b7..28dc680ff1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java @@ -1160,11 +1160,11 @@ public final int hashCode() { } /** - * Retrieve a {@link Optional}<{@link SlimefunItem}> by its id. + * Retrieve a {@link Optional} {@link SlimefunItem} by its id. * * @param id * The id of the {@link SlimefunItem} - * @return The {@link Optional}<{@link SlimefunItem}> associated with that id. Empty if non-existent + * @return The {@link Optional} {@link SlimefunItem} associated with that id. Empty if non-existent */ public static @Nonnull Optional getOptionalById(@Nonnull String id) { return Optional.ofNullable(getById(id)); @@ -1193,11 +1193,11 @@ public final int hashCode() { } /** - * Retrieve a {@link Optional}<{@link SlimefunItem}> from an {@link ItemStack}. + * Retrieve a {@link Optional} {@link SlimefunItem} from an {@link ItemStack}. * * @param item * The {@link ItemStack} to check - * @return The {@link Optional}<{@link SlimefunItem}> associated with this {@link ItemStack} if present, otherwise empty + * @return The {@link Optional} {@link SlimefunItem} associated with this {@link ItemStack} if present, otherwise empty */ public @Nonnull Optional getOptionalByItem(@Nullable ItemStack item) { return Optional.ofNullable(getByItem(item)); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java index 4960a7c9b3..c0886778df 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java @@ -2,10 +2,13 @@ import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Bukkit; import org.bukkit.entity.HumanEntity; @@ -34,13 +37,22 @@ public class PlayerBackpack { private static final String CONFIG_PREFIX = "backpacks."; - private final PlayerProfile profile; + private final UUID ownerId; private final int id; - private final Config cfg; + @Deprecated + private PlayerProfile profile; + @Deprecated + private Config cfg; private Inventory inventory; private int size; + private PlayerBackpack(@Nonnull UUID ownerId, int id, int size) { + this.ownerId = ownerId; + this.id = id; + this.size = size; + } + /** * This constructor loads an existing Backpack * @@ -48,7 +60,10 @@ public class PlayerBackpack { * The {@link PlayerProfile} of this Backpack * @param id * The id of this Backpack + * + * @deprecated Use {@link PlayerBackpack#load(UUID, int, int, HashMap)} instead */ + @Deprecated public PlayerBackpack(@Nonnull PlayerProfile profile, int id) { this(profile, id, profile.getConfig().getInt(CONFIG_PREFIX + id + ".size")); @@ -66,12 +81,16 @@ public PlayerBackpack(@Nonnull PlayerProfile profile, int id) { * The id of this Backpack * @param size * The size of this Backpack + * + * @deprecated Use {@link PlayerBackpack#newBackpack(UUID, int, int)} instead */ + @Deprecated public PlayerBackpack(@Nonnull PlayerProfile profile, int id, int size) { if (size < 9 || size > 54 || size % 9 != 0) { throw new IllegalArgumentException("Invalid size! Size must be one of: [9, 18, 27, 36, 45, 54]"); } + this.ownerId = profile.getUUID(); this.profile = profile; this.id = id; this.cfg = profile.getConfig(); @@ -96,10 +115,17 @@ public int getId() { * This method returns the {@link PlayerProfile} this {@link PlayerBackpack} belongs to * * @return The owning {@link PlayerProfile} + * + * @deprecated Use {@link PlayerBackpack#getOwnerId()} instead */ + @Deprecated @Nonnull public PlayerProfile getOwner() { - return profile; + return profile != null ? profile : PlayerProfile.find(Bukkit.getOfflinePlayer(ownerId)).orElse(null); + } + + public UUID getOwnerId() { + return this.ownerId; } /** @@ -172,7 +198,6 @@ public void setSize(int size) { } this.size = size; - cfg.setValue(CONFIG_PREFIX + id + ".size", size); Inventory inv = Bukkit.createInventory(null, size, "Backpack [" + size + " Slots]"); @@ -187,7 +212,10 @@ public void setSize(int size) { /** * This method will save the contents of this backpack to a {@link File}. + * + * @deprecated Handled by {@link PlayerProfile#save()} now */ + @Deprecated public void save() { for (int i = 0; i < size; i++) { cfg.setValue(CONFIG_PREFIX + id + ".contents." + i, inventory.getItem(i)); @@ -199,7 +227,40 @@ public void save() { * using {@link PlayerBackpack#save()} */ public void markDirty() { - profile.markDirty(); + if (profile != null) { + profile.markDirty(); + } } + private void setContents(int size, HashMap contents) { + if (this.inventory == null) { + this.inventory = Bukkit.createInventory(null, size, "Backpack [" + size + " Slots]"); + } + + for (int i = 0; i < size; i++) { + this.inventory.setItem(i, contents.get(i)); + } + } + + @ParametersAreNonnullByDefault + public static PlayerBackpack load(UUID ownerId, int id, int size, HashMap contents) { + PlayerBackpack backpack = new PlayerBackpack(ownerId, id, size); + + backpack.setContents(size, contents); + + return backpack; + } + + @ParametersAreNonnullByDefault + public static PlayerBackpack newBackpack(UUID ownerId, int id, int size) { + if (size < 9 || size > 54 || size % 9 != 0) { + throw new IllegalArgumentException("Invalid size! Size must be one of: [9, 18, 27, 36, 45, 54]"); + } + + PlayerBackpack backpack = new PlayerBackpack(ownerId, id, size); + + backpack.setContents(size, new HashMap<>()); + + return backpack; + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java index 7b975f5702..c51888a1d0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java @@ -1,27 +1,22 @@ package io.github.thebusybiscuit.slimefun4.api.player; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -import java.util.logging.Level; import java.util.stream.IntStream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import io.github.thebusybiscuit.slimefun4.storage.data.PlayerData; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; import org.bukkit.NamespacedKey; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; @@ -59,50 +54,26 @@ */ public class PlayerProfile { - private final UUID uuid; + private final UUID ownerId; private final String name; private final Config configFile; - private final Config waypointsFile; private boolean dirty = false; private boolean markedForDeletion = false; - private final Set researches = new HashSet<>(); - private final List waypoints = new ArrayList<>(); - private final Map backpacks = new HashMap<>(); private final GuideHistory guideHistory = new GuideHistory(this); private final HashedArmorpiece[] armor = { new HashedArmorpiece(), new HashedArmorpiece(), new HashedArmorpiece(), new HashedArmorpiece() }; - protected PlayerProfile(@Nonnull OfflinePlayer p) { - this.uuid = p.getUniqueId(); - this.name = p.getName(); + private final PlayerData data; - configFile = new Config("data-storage/Slimefun/Players/" + uuid.toString() + ".yml"); - waypointsFile = new Config("data-storage/Slimefun/waypoints/" + uuid.toString() + ".yml"); + protected PlayerProfile(@Nonnull OfflinePlayer p, PlayerData data) { + this.ownerId = p.getUniqueId(); + this.name = p.getName(); + this.data = data; - loadProfileData(); - } - - private void loadProfileData() { - for (Research research : Slimefun.getRegistry().getResearches()) { - if (configFile.contains("researches." + research.getID())) { - researches.add(research); - } - } - - for (String key : waypointsFile.getKeys()) { - try { - if (waypointsFile.contains(key + ".world") && Bukkit.getWorld(waypointsFile.getString(key + ".world")) != null) { - String waypointName = waypointsFile.getString(key + ".name"); - Location loc = waypointsFile.getLocation(key); - waypoints.add(new Waypoint(this, key, loc, waypointName)); - } - } catch (Exception x) { - Slimefun.logger().log(Level.WARNING, x, () -> "Could not load Waypoint \"" + key + "\" for Player \"" + name + '"'); - } - } + configFile = new Config("data-storage/Slimefun/Players/" + ownerId.toString() + ".yml"); } /** @@ -131,7 +102,7 @@ private void loadProfileData() { * @return The {@link UUID} of our {@link PlayerProfile} */ public @Nonnull UUID getUUID() { - return uuid; + return ownerId; } /** @@ -157,12 +128,7 @@ public boolean isDirty() { * This method will save the Player's Researches and Backpacks to the hard drive */ public void save() { - for (PlayerBackpack backpack : backpacks.values()) { - backpack.save(); - } - - waypointsFile.save(); - configFile.save(); + Slimefun.getPlayerStorage().savePlayerData(this.ownerId, this.data); dirty = false; } @@ -180,11 +146,9 @@ public void setResearched(@Nonnull Research research, boolean unlock) { dirty = true; if (unlock) { - configFile.setValue("researches." + research.getID(), true); - researches.add(research); + data.addResearch(research); } else { - configFile.setValue("researches." + research.getID(), null); - researches.remove(research); + data.removeResearch(research); } } @@ -202,7 +166,7 @@ public boolean hasUnlocked(@Nullable Research research) { return true; } - return !research.isEnabled() || researches.contains(research); + return !research.isEnabled() || data.getResearches().contains(research); } /** @@ -228,7 +192,7 @@ public boolean hasUnlockedEverything() { * @return A {@code Hashset} of all Researches this {@link Player} has unlocked */ public @Nonnull Set getResearches() { - return ImmutableSet.copyOf(researches); + return ImmutableSet.copyOf(this.data.getResearches()); } /** @@ -238,7 +202,7 @@ public boolean hasUnlockedEverything() { * @return A {@link List} containing every {@link Waypoint} */ public @Nonnull List getWaypoints() { - return ImmutableList.copyOf(waypoints); + return ImmutableList.copyOf(this.data.getWaypoints()); } /** @@ -249,21 +213,8 @@ public boolean hasUnlockedEverything() { * The {@link Waypoint} to add */ public void addWaypoint(@Nonnull Waypoint waypoint) { - Validate.notNull(waypoint, "Cannot add a 'null' waypoint!"); - - for (Waypoint wp : waypoints) { - if (wp.getId().equals(waypoint.getId())) { - throw new IllegalArgumentException("A Waypoint with that id already exists for this Player"); - } - } - - if (waypoints.size() < 21) { - waypoints.add(waypoint); - - waypointsFile.setValue(waypoint.getId(), waypoint.getLocation()); - waypointsFile.setValue(waypoint.getId() + ".name", waypoint.getName()); - markDirty(); - } + this.data.addWaypoint(waypoint); + markDirty(); } /** @@ -274,12 +225,8 @@ public void addWaypoint(@Nonnull Waypoint waypoint) { * The {@link Waypoint} to remove */ public void removeWaypoint(@Nonnull Waypoint waypoint) { - Validate.notNull(waypoint, "Cannot remove a 'null' waypoint!"); - - if (waypoints.remove(waypoint)) { - waypointsFile.setValue(waypoint.getId(), null); - markDirty(); - } + this.data.removeWaypoint(waypoint); + markDirty(); } /** @@ -301,8 +248,10 @@ public final void markDirty() { IntStream stream = IntStream.iterate(0, i -> i + 1).filter(i -> !configFile.contains("backpacks." + i + ".size")); int id = stream.findFirst().getAsInt(); - PlayerBackpack backpack = new PlayerBackpack(this, id, size); - backpacks.put(id, backpack); + PlayerBackpack backpack = PlayerBackpack.newBackpack(this.ownerId, id, size); + this.data.addBackpack(backpack); + + markDirty(); return backpack; } @@ -312,13 +261,10 @@ public final void markDirty() { throw new IllegalArgumentException("Backpacks cannot have negative ids!"); } - PlayerBackpack backpack = backpacks.get(id); + PlayerBackpack backpack = data.getBackpack(id); if (backpack != null) { - return Optional.of(backpack); - } else if (configFile.contains("backpacks." + id + ".size")) { - backpack = new PlayerBackpack(this, id); - backpacks.put(id, backpack); + markDirty(); return Optional.of(backpack); } @@ -346,7 +292,7 @@ private int countNonEmptyResearches(@Nonnull Collection researches) { List titles = Slimefun.getRegistry().getResearchRanks(); int allResearches = countNonEmptyResearches(Slimefun.getRegistry().getResearches()); - float fraction = (float) countNonEmptyResearches(researches) / allResearches; + float fraction = (float) countNonEmptyResearches(getResearches()) / allResearches; int index = (int) (fraction * (titles.size() - 1)); return titles.get(index); @@ -420,7 +366,9 @@ public static boolean get(@Nonnull OfflinePlayer p, @Nonnull Consumer { - AsyncProfileLoadEvent event = new AsyncProfileLoadEvent(new PlayerProfile(p)); + PlayerData data = Slimefun.getPlayerStorage().loadPlayerData(p.getUniqueId()); + + AsyncProfileLoadEvent event = new AsyncProfileLoadEvent(new PlayerProfile(p, data)); Bukkit.getPluginManager().callEvent(event); Slimefun.getRegistry().getPlayerProfiles().put(uuid, event.getProfile()); @@ -445,7 +393,9 @@ public static boolean request(@Nonnull OfflinePlayer p) { if (!Slimefun.getRegistry().getPlayerProfiles().containsKey(p.getUniqueId())) { // Should probably prevent multiple requests for the same profile in the future Bukkit.getScheduler().runTaskAsynchronously(Slimefun.instance(), () -> { - PlayerProfile pp = new PlayerProfile(p); + PlayerData data = Slimefun.getPlayerStorage().loadPlayerData(p.getUniqueId()); + + PlayerProfile pp = new PlayerProfile(p, data); Slimefun.getRegistry().getPlayerProfiles().put(p.getUniqueId(), pp); }); @@ -527,19 +477,23 @@ public boolean hasFullProtectionAgainst(@Nonnull ProtectionType type) { return armorCount == 4; } + public PlayerData getPlayerData() { + return this.data; + } + @Override public int hashCode() { - return uuid.hashCode(); + return ownerId.hashCode(); } @Override public boolean equals(Object obj) { - return obj instanceof PlayerProfile profile && uuid.equals(profile.uuid); + return obj instanceof PlayerProfile profile && ownerId.equals(profile.ownerId); } @Override public String toString() { - return "PlayerProfile {" + uuid + "}"; + return "PlayerProfile {" + ownerId + "}"; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java index e8c7b460a1..8667eed682 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java @@ -15,6 +15,9 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import io.github.thebusybiscuit.slimefun4.storage.Storage; +import io.github.thebusybiscuit.slimefun4.storage.backend.legacy.LegacyStorage; + import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Server; @@ -197,6 +200,9 @@ public final class Slimefun extends JavaPlugin implements SlimefunAddon { private final Config items = new Config(this, "Items.yml"); private final Config researches = new Config(this, "Researches.yml"); + // Data storage + private Storage playerStorage; + // Listeners that need to be accessed elsewhere private final GrapplingHookListener grapplingHookListener = new GrapplingHookListener(); private final BackpackListener backpackListener = new BackpackListener(); @@ -258,6 +264,9 @@ private void onUnitTestStart() { registry.load(this, config); loadTags(); soundService.reload(false); + // TODO: What do we do if tests want to use another storage backend (e.g. testing new feature on legacy + sql)? + // Do we have a way to override this? + playerStorage = new LegacyStorage(); } /** @@ -312,6 +321,10 @@ private void onPluginStart() { networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"), config.getBoolean("networks.delete-excess-items")); + // Data storage + playerStorage = new LegacyStorage(); + logger.log(Level.INFO, "Using legacy storage for player data"); + // Setting up bStats new Thread(metricsService::start, "Slimefun Metrics").start(); @@ -1068,4 +1081,7 @@ public static boolean isNewlyInstalled() { return instance.getServer().getScheduler().runTask(instance, runnable); } + public static @Nonnull Storage getPlayerStorage() { + return instance().playerStorage; + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/storage/Storage.java b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/Storage.java new file mode 100644 index 0000000000..037db2afc3 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/Storage.java @@ -0,0 +1,26 @@ +package io.github.thebusybiscuit.slimefun4.storage; + +import io.github.thebusybiscuit.slimefun4.storage.data.PlayerData; + +import javax.annotation.concurrent.ThreadSafe; + +import com.google.common.annotations.Beta; + +import java.util.UUID; + +/** + * The {@link Storage} interface is the abstract layer on top of our storage backends. + * Every backend has to implement this interface and has to implement it in a thread-safe way. + * There will be no expectation of running functions in here within the main thread. + * + *

+ * This API is still experimental, it may change without notice. + */ +@Beta +@ThreadSafe +public interface Storage { + + PlayerData loadPlayerData(UUID uuid); + + void savePlayerData(UUID uuid, PlayerData data); +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/storage/backend/legacy/LegacyStorage.java b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/backend/legacy/LegacyStorage.java new file mode 100644 index 0000000000..d7981a5466 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/backend/legacy/LegacyStorage.java @@ -0,0 +1,127 @@ +package io.github.thebusybiscuit.slimefun4.storage.backend.legacy; + +import io.github.bakedlibs.dough.config.Config; +import io.github.thebusybiscuit.slimefun4.api.gps.Waypoint; +import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack; +import io.github.thebusybiscuit.slimefun4.api.researches.Research; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.storage.Storage; +import io.github.thebusybiscuit.slimefun4.storage.data.PlayerData; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +import com.google.common.annotations.Beta; + +import javax.annotation.Nonnull; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +@Beta +public class LegacyStorage implements Storage { + + @Override + public PlayerData loadPlayerData(@Nonnull UUID uuid) { + Config playerFile = new Config("data-storage/Slimefun/Players/" + uuid + ".yml"); + // Not too sure why this is its own file + Config waypointsFile = new Config("data-storage/Slimefun/waypoints/" + uuid + ".yml"); + + // Load research + Set researches = new HashSet<>(); + for (Research research : Slimefun.getRegistry().getResearches()) { + if (playerFile.contains("researches." + research.getID())) { + researches.add(research); + } + } + + // Load backpacks + HashMap backpacks = new HashMap<>(); + for (String key : playerFile.getKeys("backpacks")) { + try { + int id = Integer.parseInt(key); + int size = playerFile.getInt("backpacks." + key + ".size"); + + HashMap items = new HashMap<>(); + for (int i = 0; i < size; i++) { + items.put(i, playerFile.getItem("backpacks." + key + ".contents." + i)); + } + + PlayerBackpack backpack = PlayerBackpack.load(uuid, id, size, items); + + backpacks.put(id, backpack); + } catch (Exception x) { + Slimefun.logger().log(Level.WARNING, x, () -> "Could not load Backpack \"" + key + "\" for Player \"" + uuid + '"'); + } + } + + // Load waypoints + Set waypoints = new HashSet<>(); + for (String key : waypointsFile.getKeys()) { + try { + if (waypointsFile.contains(key + ".world") && Bukkit.getWorld(waypointsFile.getString(key + ".world")) != null) { + String waypointName = waypointsFile.getString(key + ".name"); + Location loc = waypointsFile.getLocation(key); + waypoints.add(new Waypoint(uuid, key, loc, waypointName)); + } + } catch (Exception x) { + Slimefun.logger().log(Level.WARNING, x, () -> "Could not load Waypoint \"" + key + "\" for Player \"" + uuid + '"'); + } + } + + return new PlayerData(researches, backpacks, waypoints); + } + + // The current design of saving all at once isn't great, this will be refined. + @Override + public void savePlayerData(@Nonnull UUID uuid, @Nonnull PlayerData data) { + Config playerFile = new Config("data-storage/Slimefun/Players/" + uuid + ".yml"); + // Not too sure why this is its own file + Config waypointsFile = new Config("data-storage/Slimefun/waypoints/" + uuid + ".yml"); + + // Save research + playerFile.setValue("rearches", null); + for (Research research : Slimefun.getRegistry().getResearches()) { + // Save the research if it's researched + if (data.getResearches().contains(research)) { + playerFile.setValue("researches." + research.getID(), true); + + // Remove the research if it's no longer researched + } else if (playerFile.contains("researches." + research.getID())) { + playerFile.setValue("researches." + research.getID(), null); + } + } + + // Save backpacks + for (PlayerBackpack backpack : data.getBackpacks().values()) { + playerFile.setValue("backpacks." + backpack.getId() + ".size", backpack.getSize()); + + for (int i = 0; i < backpack.getSize(); i++) { + ItemStack item = backpack.getInventory().getItem(i); + if (item != null) { + playerFile.setValue("backpacks." + backpack.getId() + ".contents." + i, item); + + // Remove the item if it's no longer in the inventory + } else if (playerFile.contains("backpacks." + backpack.getId() + ".contents." + i)) { + playerFile.setValue("backpacks." + backpack.getId() + ".contents." + i, null); + } + } + } + + // Save waypoints + waypointsFile.clear(); + for (Waypoint waypoint : data.getWaypoints()) { + // Legacy data uses IDs + waypointsFile.setValue(waypoint.getId(), waypoint.getLocation()); + waypointsFile.setValue(waypoint.getId() + ".name", waypoint.getName()); + } + + // Save files + playerFile.save(); + waypointsFile.save(); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/storage/data/PlayerData.java b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/data/PlayerData.java new file mode 100644 index 0000000000..8615b6ee5f --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/storage/data/PlayerData.java @@ -0,0 +1,96 @@ +package io.github.thebusybiscuit.slimefun4.storage.data; + +import com.google.common.annotations.Beta; + +import io.github.thebusybiscuit.slimefun4.api.gps.Waypoint; +import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack; +import io.github.thebusybiscuit.slimefun4.api.researches.Research; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang.Validate; + +/** + * The data which backs {@link io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile} + * + * This API is still experimental, it may change without notice. + */ +// TODO: Should we keep this in PlayerProfile? +@Beta +public class PlayerData { + + private final Set researches = new HashSet<>(); + private final Map backpacks = new HashMap<>(); + private final Set waypoints = new HashSet<>(); + + public PlayerData(Set researches, Map backpacks, Set waypoints) { + this.researches.addAll(researches); + this.backpacks.putAll(backpacks); + this.waypoints.addAll(waypoints); + } + + public Set getResearches() { + return researches; + } + + public void addResearch(@Nonnull Research research) { + Validate.notNull(research, "Cannot add a 'null' research!"); + researches.add(research); + } + + public void removeResearch(@Nonnull Research research) { + Validate.notNull(research, "Cannot remove a 'null' research!"); + researches.remove(research); + } + + @Nonnull + public Map getBackpacks() { + return backpacks; + } + + @Nonnull + public PlayerBackpack getBackpack(int id) { + return backpacks.get(id); + } + + public void addBackpack(@Nonnull PlayerBackpack backpack) { + Validate.notNull(backpack, "Cannot add a 'null' backpack!"); + backpacks.put(backpack.getId(), backpack); + } + + public void removeBackpack(@Nonnull PlayerBackpack backpack) { + Validate.notNull(backpack, "Cannot remove a 'null' backpack!"); + backpacks.remove(backpack.getId()); + } + + public Set getWaypoints() { + return waypoints; + } + + public void addWaypoint(@Nonnull Waypoint waypoint) { + Validate.notNull(waypoint, "Cannot add a 'null' waypoint!"); + + for (Waypoint wp : waypoints) { + if (wp.getId().equals(waypoint.getId())) { + throw new IllegalArgumentException("A Waypoint with that id already exists for this Player"); + } + } + + // Limited to 21 due to limited UI space and no pagination + if (waypoints.size() >= 21) { + return; // not sure why this doesn't throw but the one above does... + } + + waypoints.add(waypoint); + } + + public void removeWaypoint(@Nonnull Waypoint waypoint) { + Validate.notNull(waypoint, "Cannot remove a 'null' waypoint!"); + waypoints.remove(waypoint); + } +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/api/gps/TestWaypoints.java b/src/test/java/io/github/thebusybiscuit/slimefun4/api/gps/TestWaypoints.java index d115135ba7..a0de64b14f 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/api/gps/TestWaypoints.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/api/gps/TestWaypoints.java @@ -1,5 +1,9 @@ package io.github.thebusybiscuit.slimefun4.api.gps; +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; import org.bukkit.entity.Player; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -19,16 +23,20 @@ class TestWaypoints { private static ServerMock server; private static Slimefun plugin; + private static File dataFolder; @BeforeAll public static void load() { server = MockBukkit.mock(); plugin = MockBukkit.load(Slimefun.class); + dataFolder = new File("data-storage/Slimefun/waypoints"); + dataFolder.mkdirs(); } @AfterAll - public static void unload() { + public static void unload() throws IOException { MockBukkit.unmock(); + FileUtils.deleteDirectory(dataFolder); } @Test @@ -38,9 +46,8 @@ void testAddWaypointToProfile() throws InterruptedException { PlayerProfile profile = TestUtilities.awaitProfile(player); Assertions.assertTrue(profile.getWaypoints().isEmpty()); - Waypoint waypoint = new Waypoint(profile, "hello", player.getLocation(), "HELLO"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), "hello", player.getLocation(), "HELLO"); profile.addWaypoint(waypoint); - Assertions.assertTrue(profile.isDirty()); Assertions.assertThrows(IllegalArgumentException.class, () -> profile.addWaypoint(null)); @@ -55,7 +62,7 @@ void testRemoveWaypointFromProfile() throws InterruptedException { Player player = server.addPlayer(); PlayerProfile profile = TestUtilities.awaitProfile(player); - Waypoint waypoint = new Waypoint(profile, "hello", player.getLocation(), "HELLO"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), "hello", player.getLocation(), "HELLO"); profile.addWaypoint(waypoint); Assertions.assertEquals(1, profile.getWaypoints().size()); @@ -76,7 +83,7 @@ void testWaypointAlreadyExisting() throws InterruptedException { Player player = server.addPlayer(); PlayerProfile profile = TestUtilities.awaitProfile(player); - Waypoint waypoint = new Waypoint(profile, "test", player.getLocation(), "Testing"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), "test", player.getLocation(), "Testing"); profile.addWaypoint(waypoint); Assertions.assertEquals(1, profile.getWaypoints().size()); @@ -91,7 +98,7 @@ void testTooManyWaypoints() throws InterruptedException { PlayerProfile profile = TestUtilities.awaitProfile(player); for (int i = 0; i < 99; i++) { - Waypoint waypoint = new Waypoint(profile, String.valueOf(i), player.getLocation(), "Test"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), String.valueOf(i), player.getLocation(), "Test"); profile.addWaypoint(waypoint); } @@ -114,11 +121,10 @@ void testWaypointEvent() throws InterruptedException { @DisplayName("Test equal Waypoints being equal") void testWaypointComparison() throws InterruptedException { Player player = server.addPlayer(); - PlayerProfile profile = TestUtilities.awaitProfile(player); - Waypoint waypoint = new Waypoint(profile, "waypoint", player.getLocation(), "Test"); - Waypoint same = new Waypoint(profile, "waypoint", player.getLocation(), "Test"); - Waypoint different = new Waypoint(profile, "waypoint_nope", player.getLocation(), "Test2"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), "waypoint", player.getLocation(), "Test"); + Waypoint same = new Waypoint(player.getUniqueId(), "waypoint", player.getLocation(), "Test"); + Waypoint different = new Waypoint(player.getUniqueId(), "waypoint_nope", player.getLocation(), "Test2"); Assertions.assertEquals(waypoint, same); Assertions.assertEquals(waypoint.hashCode(), same.hashCode()); @@ -131,10 +137,9 @@ void testWaypointComparison() throws InterruptedException { @DisplayName("Test Deathpoints being recognized as Deathpoints") void testIsDeathpoint() throws InterruptedException { Player player = server.addPlayer(); - PlayerProfile profile = TestUtilities.awaitProfile(player); - Waypoint waypoint = new Waypoint(profile, "waypoint", player.getLocation(), "Some Waypoint"); - Waypoint deathpoint = new Waypoint(profile, "deathpoint", player.getLocation(), "player:death I died"); + Waypoint waypoint = new Waypoint(player.getUniqueId(), "waypoint", player.getLocation(), "Some Waypoint"); + Waypoint deathpoint = new Waypoint(player.getUniqueId(), "deathpoint", player.getLocation(), "player:death I died"); Assertions.assertFalse(waypoint.isDeathpoint()); Assertions.assertTrue(deathpoint.isDeathpoint()); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/api/profiles/TestPlayerBackpacks.java b/src/test/java/io/github/thebusybiscuit/slimefun4/api/profiles/TestPlayerBackpacks.java index 3c1ae7175c..07f69761e0 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/api/profiles/TestPlayerBackpacks.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/api/profiles/TestPlayerBackpacks.java @@ -2,9 +2,7 @@ import java.util.Optional; -import org.bukkit.Material; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -39,16 +37,12 @@ public static void unload() { void testCreateBackpack() throws InterruptedException { Player player = server.addPlayer(); PlayerProfile profile = TestUtilities.awaitProfile(player); - Assertions.assertFalse(profile.isDirty()); PlayerBackpack backpack = profile.createBackpack(18); Assertions.assertNotNull(backpack); - // Creating a backpack should mark profiles as dirty - Assertions.assertTrue(profile.isDirty()); - - Assertions.assertEquals(profile, backpack.getOwner()); + Assertions.assertEquals(player.getUniqueId(), backpack.getOwnerId()); Assertions.assertEquals(18, backpack.getSize()); Assertions.assertEquals(18, backpack.getInventory().getSize()); } @@ -71,7 +65,6 @@ void testChangeSize() throws InterruptedException { backpack.setSize(27); Assertions.assertEquals(27, backpack.getSize()); - Assertions.assertTrue(profile.isDirty()); } @Test @@ -90,33 +83,4 @@ void testGetBackpackById() throws InterruptedException { Assertions.assertFalse(profile.getBackpack(500).isPresent()); } - - @Test - @DisplayName("Test loading a backpack from file") - void testLoadBackpackFromFile() throws InterruptedException { - Player player = server.addPlayer(); - PlayerProfile profile = TestUtilities.awaitProfile(player); - - profile.getConfig().setValue("backpacks.50.size", 27); - - for (int i = 0; i < 27; i++) { - profile.getConfig().setValue("backpacks.50.contents." + i, new ItemStack(Material.DIAMOND)); - } - - Optional optional = profile.getBackpack(50); - Assertions.assertTrue(optional.isPresent()); - - PlayerBackpack backpack = optional.get(); - Assertions.assertEquals(50, backpack.getId()); - Assertions.assertEquals(27, backpack.getSize()); - Assertions.assertEquals(-1, backpack.getInventory().firstEmpty()); - - backpack.getInventory().setItem(1, new ItemStack(Material.NETHER_STAR)); - - Assertions.assertEquals(new ItemStack(Material.DIAMOND), profile.getConfig().getItem("backpacks.50.contents.1")); - - // Saving should write it to the Config file - backpack.save(); - Assertions.assertEquals(new ItemStack(Material.NETHER_STAR), profile.getConfig().getItem("backpacks.50.contents.1")); - } } diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestBackpackListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestBackpackListener.java index 01e83b9e71..443d47c3c7 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestBackpackListener.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TestBackpackListener.java @@ -13,7 +13,6 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryType.SlotType; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.inventory.Inventory; @@ -119,7 +118,7 @@ void testSetId() throws InterruptedException { Assertions.assertEquals(ChatColor.GRAY + "ID: " + player.getUniqueId() + "#" + id, item.getItemMeta().getLore().get(2)); PlayerBackpack backpack = awaitBackpack(item); - Assertions.assertEquals(player.getUniqueId(), backpack.getOwner().getUUID()); + Assertions.assertEquals(player.getUniqueId(), backpack.getOwnerId()); Assertions.assertEquals(id, backpack.getId()); } @@ -132,16 +131,6 @@ void testOpenBackpack() throws InterruptedException { Assertions.assertEquals(backpack.getInventory(), view.getTopInventory()); } - @Test - @DisplayName("Test backpacks being marked dirty on close") - void testCloseBackpack() throws InterruptedException { - Player player = server.addPlayer(); - PlayerBackpack backpack = openMockBackpack(player, "TEST_CLOSE_BACKPACK", 27); - listener.onClose(new InventoryCloseEvent(player.getOpenInventory())); - - Assertions.assertTrue(backpack.getOwner().isDirty()); - } - @Test @DisplayName("Test backpacks not disturbing normal item dropping") void testBackpackDropNormalItem() throws InterruptedException { diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/storage/backend/TestLegacyBackend.java b/src/test/java/io/github/thebusybiscuit/slimefun4/storage/backend/TestLegacyBackend.java new file mode 100644 index 0000000000..1859659999 --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/storage/backend/TestLegacyBackend.java @@ -0,0 +1,383 @@ +package io.github.thebusybiscuit.slimefun4.storage.backend; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.World.Environment; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.thebusybiscuit.slimefun4.api.gps.Waypoint; +import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.api.researches.Research; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.storage.backend.legacy.LegacyStorage; +import io.github.thebusybiscuit.slimefun4.storage.data.PlayerData; +import io.github.thebusybiscuit.slimefun4.test.TestUtilities; +import net.md_5.bungee.api.ChatColor; + +class TestLegacyBackend { + + private static ServerMock server; + private static Slimefun plugin; + + @BeforeAll + public static void load() { + server = MockBukkit.mock(); + plugin = MockBukkit.load(Slimefun.class); + + File playerFolder = new File("data-storage/Slimefun/Players"); + playerFolder.mkdirs(); + File waypointFolder = new File("data-storage/Slimefun/waypoints"); + waypointFolder.mkdirs(); + + // Not too sure why this is needed, we don't use it elsewhere, it should just use the ItemStack serialization + // My guess is MockBukkit isn't loading the ConfigurationSerialization class therefore the static block + // within the class isn't being fired (where ItemStack and other classes are registered) + ConfigurationSerialization.registerClass(ItemStack.class); + ConfigurationSerialization.registerClass(ItemMeta.class); + + setupResearches(); + } + + @AfterAll + public static void unload() throws IOException { + MockBukkit.unmock(); + FileUtils.deleteDirectory(new File("data-storage")); + } + + // Test simple loading and saving of player data + @Test + void testLoadingResearches() throws IOException { + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + Files.writeString(playerFile.toPath(), """ + researches: + '0': true + '1': true + '2': true + '3': true + '4': true + '5': true + '6': true + '7': true + '8': true + '9': true + """); + + // Load the player data + LegacyStorage storage = new LegacyStorage(); + PlayerData data = storage.loadPlayerData(uuid); + + // Check if the data is correct + Assertions.assertEquals(10, data.getResearches().size()); + for (int i = 0; i < 10; i++) { + Assertions.assertTrue(data.getResearches().contains(Slimefun.getRegistry().getResearches().get(i))); + } + } + + // There's some issues with deserializing items in tests, I spent quite a while debugging this + // and didn't really get anywhere. So commenting this out for now. + /* + @Test + void testLoadingBackpacks() throws IOException { + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + Files.writeString(playerFile.toPath(), """ + backpacks: + '0': + size: 9 + contents: + '0': + ==: org.bukkit.inventory.ItemStack + v: 1 + type: IRON_BLOCK + meta: + ==: org.bukkit.inventory.meta.ItemMeta + enchants: {} + damage: 0 + persistentDataContainer: + slimefun:slimefun_item: TEST + displayName: ยง6Test block + itemFlags: !!set {} + unbreakable: false + repairCost: 0 + """); + + // Load the player data + LegacyStorage storage = new LegacyStorage(); + PlayerData data = storage.loadPlayerData(uuid); + + // Check if the data is correct + Assertions.assertEquals(1, data.getBackpacks().size()); + Assertions.assertEquals(9, data.getBackpacks().get(0).getSize()); + + // Validate item deserialization + System.out.println( + Arrays.stream(data.getBackpack(0).getInventory().getContents()) + .map((item) -> item == null ? "null" : item.getType().name()) + .collect(Collectors.joining(", ")) + ); + ItemStack stack = data.getBackpack(0).getInventory().getItem(0); + Assertions.assertNotNull(stack); + Assertions.assertEquals("IRON_BLOCK", stack.getType().name()); + Assertions.assertEquals(1, stack.getAmount()); + Assertions.assertEquals(ChatColor.GREEN + "Test block", stack.getItemMeta().getDisplayName()); + } + */ + + @Test + void testLoadingWaypoints() throws IOException { + // Create mock world + server.createWorld(WorldCreator.name("world").environment(Environment.NORMAL)); + + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File waypointFile = new File("data-storage/Slimefun/waypoints/" + uuid + ".yml"); + Files.writeString(waypointFile.toPath(), """ + TEST: + x: -173.0 + y: 75.0 + z: -11.0 + pitch: 0.0 + yaw: 178.0 + world: world + name: test + """); + + // Load the player data + LegacyStorage storage = new LegacyStorage(); + PlayerData data = storage.loadPlayerData(uuid); + + // Check if the data is correct + Assertions.assertEquals(1, data.getWaypoints().size()); + + // Validate waypoint deserialization + Waypoint waypoint = data.getWaypoints().iterator().next(); + + Assertions.assertEquals("test", waypoint.getName()); + Assertions.assertEquals(-173.0, waypoint.getLocation().getX()); + Assertions.assertEquals(75.0, waypoint.getLocation().getY()); + Assertions.assertEquals(-11.0, waypoint.getLocation().getZ()); + Assertions.assertEquals(178.0, waypoint.getLocation().getYaw()); + Assertions.assertEquals(0.0, waypoint.getLocation().getPitch()); + Assertions.assertEquals("world", waypoint.getLocation().getWorld().getName()); + } + + @Test + void testSavingResearches() throws InterruptedException { + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + + PlayerProfile profile = TestUtilities.awaitProfile(player); + + for (Research research : Slimefun.getRegistry().getResearches()) { + profile.setResearched(research, true); + } + + // Save the player data + LegacyStorage storage = new LegacyStorage(); + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + PlayerData assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(10, assertion.getResearches().size()); + for (int i = 0; i < 10; i++) { + Assertions.assertTrue(assertion.getResearches().contains(Slimefun.getRegistry().getResearches().get(i))); + } + } + + // There's some issues with deserializing items in tests, I spent quite a while debugging this + // and didn't really get anywhere. So commenting this out for now. + /* + @Test + void testSavingBackpacks() throws InterruptedException { + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + + PlayerProfile profile = TestUtilities.awaitProfile(player); + + PlayerBackpack backpack = profile.createBackpack(9); + backpack.getInventory().addItem(SlimefunItems.AIR_RUNE); + + // Save the player data + LegacyStorage storage = new LegacyStorage(); + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + PlayerData assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(1, assertion.getBackpacks().size()); + } + */ + + @Test + void testSavingWaypoints() throws InterruptedException { + // Create mock world + World world = server.createWorld(WorldCreator.name("world").environment(Environment.NORMAL)); + + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + PlayerProfile profile = TestUtilities.awaitProfile(player); + + profile.addWaypoint(new Waypoint( + player.getUniqueId(), + "test", + new Location(world, 1, 2, 3, 4, 5), + ChatColor.GREEN + "Test waypoint") + ); + + // Save the player data + LegacyStorage storage = new LegacyStorage(); + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + PlayerData assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(1, assertion.getWaypoints().size()); + + // Validate waypoint deserialization + Waypoint waypoint = assertion.getWaypoints().iterator().next(); + + Assertions.assertEquals(ChatColor.GREEN + "Test waypoint", waypoint.getName()); + Assertions.assertEquals(1, waypoint.getLocation().getX()); + Assertions.assertEquals(2, waypoint.getLocation().getY()); + Assertions.assertEquals(3, waypoint.getLocation().getZ()); + Assertions.assertEquals(4, waypoint.getLocation().getYaw()); + Assertions.assertEquals(5, waypoint.getLocation().getPitch()); + Assertions.assertEquals("world", waypoint.getLocation().getWorld().getName()); + } + + // Test realistic situations + @Test + void testResearchChanges() throws InterruptedException { + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + PlayerProfile profile = TestUtilities.awaitProfile(player); + + // Unlock all researches + for (Research research : Slimefun.getRegistry().getResearches()) { + profile.setResearched(research, true); + } + + // Save the player data + LegacyStorage storage = new LegacyStorage(); + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + PlayerData assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(10, assertion.getResearches().size()); + for (int i = 0; i < 10; i++) { + Assertions.assertTrue(assertion.getResearches().contains(Slimefun.getRegistry().getResearches().get(i))); + } + + // Now let's change the data and save it again + profile.setResearched(Slimefun.getRegistry().getResearches().get(3), false); + + // Save the player data + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + System.out.println("update assertion"); + assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(9, assertion.getResearches().size()); + for (int i = 0; i < 10; i++) { + if (i != 3) { + Assertions.assertTrue(assertion.getResearches().contains(Slimefun.getRegistry().getResearches().get(i))); + } + } + } + + // Test realistic situations - when we fix the serialization issue + // @Test + // void testBackpackChanges() throws InterruptedException {} + + @Test + void testWaypointChanges() throws InterruptedException { + // Create mock world + World world = server.createWorld(WorldCreator.name("world").environment(Environment.NORMAL)); + + // Create a player file which we can load + UUID uuid = UUID.randomUUID(); + File playerFile = new File("data-storage/Slimefun/Players/" + uuid + ".yml"); + + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + PlayerProfile profile = TestUtilities.awaitProfile(player); + + profile.addWaypoint(new Waypoint( + player.getUniqueId(), + "test", + new Location(world, 1, 2, 3, 4, 5), + ChatColor.GREEN + "Test waypoint" + )); + + Waypoint test2 = new Waypoint( + player.getUniqueId(), + "test2", + new Location(world, 10, 20, 30, 40, 50), + ChatColor.GREEN + "Test 2 waypoint" + ); + profile.addWaypoint(test2); + + // Save the player data + LegacyStorage storage = new LegacyStorage(); + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + PlayerData assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(2, assertion.getWaypoints().size()); + + // Remove one + profile.removeWaypoint(test2); + + // Save the player data + storage.savePlayerData(uuid, profile.getPlayerData()); + + // Assert the file exists and data is correct + Assertions.assertTrue(playerFile.exists()); + assertion = storage.loadPlayerData(uuid); + Assertions.assertEquals(1, assertion.getWaypoints().size()); + } + + // Utils + private static void setupResearches() { + for (int i = 0; i < 10; i++) { + NamespacedKey key = new NamespacedKey(plugin, "test_" + i); + Research research = new Research(key, i, "Test " + i, 100); + research.register(); + } + } +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/test/mocks/MockProfile.java b/src/test/java/io/github/thebusybiscuit/slimefun4/test/mocks/MockProfile.java index 8afad75e12..9a7837d00a 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/test/mocks/MockProfile.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/test/mocks/MockProfile.java @@ -1,15 +1,22 @@ package io.github.thebusybiscuit.slimefun4.test.mocks; +import java.util.HashMap; +import java.util.Set; + import javax.annotation.Nonnull; import org.bukkit.OfflinePlayer; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.storage.data.PlayerData; public class MockProfile extends PlayerProfile { public MockProfile(@Nonnull OfflinePlayer p) { - super(p); + this(p, new PlayerData(Set.of(), new HashMap<>(), Set.of())); } + public MockProfile(@Nonnull OfflinePlayer p, @Nonnull PlayerData data) { + super(p, data); + } } From e33950383c38d6682e50095fdc16a559f8030ee7 Mon Sep 17 00:00:00 2001 From: J3fftw <44972470+J3fftw1@users.noreply.github.com> Date: Wed, 3 Jan 2024 20:12:45 +0100 Subject: [PATCH 66/66] temporarily diable senstive blocks check (#4077) Co-authored-by: Daniel Walsh --- .../slimefun4/implementation/listeners/BlockListener.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 1db923bebc..c214c33a2c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -303,8 +303,11 @@ private void checkForSensitiveBlockAbove(Player player, Block block, ItemStack i * @param count * The amount of times this has been recursively called */ + // Disabled for now due to #4069 - Servers crashing due to this check + // There is additionally a second bug with `getMaxChainedNeighborUpdates` not existing in 1.17 @ParametersAreNonnullByDefault private void checkForSensitiveBlocks(Block block, Integer count, boolean isDropItems) { + /* if (count >= Bukkit.getServer().getMaxChainedNeighborUpdates()) { return; } @@ -326,6 +329,7 @@ private void checkForSensitiveBlocks(Block block, Integer count, boolean isDropI // Set the BlockData back: this makes it so containers and spawners drop correctly. This is a hacky fix. block.setBlockData(state.getBlockData(), false); state.update(true, false); + */ } /**