diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index a36d88916..302ffbf67 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -48,6 +48,6 @@ assignees: '' - - Server Software: - - Minecraft Version: - - Slimefun Version: + - Server software: + - Minecraft version: + - Slimefun version: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index cfa814661..552758aa2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,16 +1,20 @@ ## Description - + + -## Changes - +## Proposed changes + + -## Related Issues +## Related Issues (if applicable) + ## Checklist - - + + + - [ ] I have fully tested the proposed changes and promise that they will not break everything into chaos. - [ ] I have also tested the proposed changes in combination with various popular addons and can confirm my changes do not break them. - [ ] I followed the existing code standards and didn't mess up the formatting. diff --git a/.github/workflows/discord-webhook.yml b/.github/workflows/discord-webhook.yml index d6edc5e08..ad787e29b 100644 --- a/.github/workflows/discord-webhook.yml +++ b/.github/workflows/discord-webhook.yml @@ -18,8 +18,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v2.3.4 - name: Set up Java JDK 11 - uses: actions/setup-java@v1.4.3 + uses: actions/setup-java@v2.0.0 with: + distribution: 'adopt' java-version: '11' java-package: jdk architecture: x64 diff --git a/.github/workflows/maven-compiler.yml b/.github/workflows/maven-compiler.yml index 9b02b166c..28d798574 100644 --- a/.github/workflows/maven-compiler.yml +++ b/.github/workflows/maven-compiler.yml @@ -24,8 +24,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 - name: Set up JDK 1.8 - uses: actions/setup-java@master + uses: actions/setup-java@v2.0.0 with: - java-version: 1.8 + distribution: 'adopt' + java-version: '8' - name: Build with Maven run: mvn package --file pom.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 55f5a1204..1838d370a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Table of contents -- [Release Candidate 22 (TBD)](#release-candidate-22-tbd) +- [Release Candidate 23 (TBD)](#release-candidate-23-tbd) +- [Release Candidate 22 (18 Apr 2021)](#release-candidate-22-18-apr-2021) - [Release Candidate 21 (14 Mar 2021)](#release-candidate-21-14-mar-2021) - [Release Candidate 20 (30 Jan 2021)](#release-candidate-20-30-jan-2021) - [Release Candidate 19 (11 Jan 2021)](#release-candidate-19-11-jan-2021) @@ -22,15 +23,46 @@ - [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 22 (TBD) +## Release Candidate 23 (TBD) #### Additions -* Added Vanilla Auto Crafter -* Added Enhanced Auto Crafter +* Added "Quartz Block -> 4 Quartz" recipe to Grind Stone + +#### Changes +* Renamed "Solar Panel" to "Photovoltaic Cell" to avoid confusions with solar generators +* Photovoltaic Cells can no longer be placed +* (API) Removed deprecated "SlimefunBlockHandler" + +#### Fixes + +## Release Candidate 22 (18 Apr 2021) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#22 + +#### Additions +* Added Vanilla Auto-Crafter +* Added Enhanced Auto-Crafter * Added "Smart-Filling" mode to Cargo Input nodes +* Added "Netherite Ingot -> Netherite Block" recipe to Electric Press +* Added "Slimeballs -> Slime Block" recipe to Electric Press +* Added Armor Forge Auto-Crafter +* Auto-Crafters can now be turned on and off +* Added Produce Collector to automate Milk and Mushroom Stew +* Added a new message when constructing a Multiblock successfully +* Added Crafting Motor +* Block Placers can now place down cake +* Added support for the "FunnyGuilds" plugin +* Added "magma cream -> slime ball" recipe to the Freezer +* Added "2 magma blocks -> slime block" recipe to the Freezer +* Added configurable enchantment level limit for both auto enchanter and auto disenchanter +* (API) Added AutoEnchantEvent #### Changes * Changed item order in guide for the Villager Rune and Nether Goo (All runes are now grouped together) +* Ancient Pedestals can now be found under "Magical Gadgets" +* Removed all functionality from the old Automated Crafting Chamber +* Changed Cargo Motor texture +* Lowered "Magma block -> Sulfate" recipe to only require 1 magma block +* Small performance improvements #### Fixes * Fixed #1161 @@ -41,6 +73,19 @@ * Fixed #2896 * Fixed #2899 * Fixed #2906 +* Fixed #2903 +* Fixed #2913 +* Fixed #2914 +* Fixed Auto-Crafters swallowing buckets when crafting cake +* Fixed Multimeter not working on Auto-Crafters +* Fixed #2650 +* Fixed Slimefun items applying damage to items with an `unbreakable` tag +* Fixed #2930 +* Fixed #2926 +* Fixed Grappling Hook vanishing in creative mode +* Fixed #2944 +* Fixed #2837 +* Fixed #2942 ## Release Candidate 21 (14 Mar 2021) https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#21 diff --git a/README.md b/README.md index e56960e74..0f3ffd88b 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Well, we asked some users on our [Discord server](#discord) to send us some scre | *Screenshot provided by GalaxyKat11#3816* | *Screenshot provided by TamThan#7987* | *Screenshot provided by Kilaruna#4981* | ## Discord -You can find Slimefun's community on Discord and connect with **over 4000** users of this plugin from all over the world.
+You can find Slimefun's community on Discord and connect with **over 5000** users of this plugin from all over the world.
Click the badge down below to join the server for suggestions/questions or other discussions about this plugin.
We are also hosting a community event every so often, join us to find out more.
**Important**: We do **not** accept bug reports on discord, please use our [Issue Tracker](https://github.com/Slimefun/Slimefun4/issues) to submit bug reports! diff --git a/pom.xml b/pom.xml index c7b07fdb7..e355832d1 100644 --- a/pom.xml +++ b/pom.xml @@ -341,13 +341,13 @@ org.mockito mockito-core - 3.8.0 + 3.9.0 test com.github.seeseemelk MockBukkit-v1.16 - 0.32.1 + 0.33.0 test @@ -364,7 +364,7 @@ com.github.TheBusyBiscuit CS-CoreLib2 - 0.30.3 + 0.30.4 compile @@ -420,7 +420,7 @@ com.gmail.nossr50.mcMMO mcMMO - 2.1.182 + 2.1.194 provided diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java index aba815064..b0673c927 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java @@ -50,6 +50,18 @@ public class ErrorReport { private T throwable; private File file; + /** + * This is the base constructor for an {@link ErrorReport}. It will only + * print the necessary info and provides a {@link Consumer} for any more detailed + * needs. + * + * @param throwable + * The {@link Throwable} which caused this {@link ErrorReport}. + * @param addon + * The {@link SlimefunAddon} responsible. + * @param printer + * A custom {@link Consumer} to add more details. + */ @ParametersAreNonnullByDefault public ErrorReport(T throwable, SlimefunAddon addon, Consumer printer) { this.throwable = throwable; @@ -58,6 +70,17 @@ public class ErrorReport { SlimefunPlugin.runSync(() -> print(printer)); } + /** + * This constructs a new {@link ErrorReport} for the given {@link Location} and + * {@link SlimefunItem}. + * + * @param throwable + * The {@link Throwable} which caused this {@link ErrorReport}. + * @param l + * The {@link Location} at which the error was thrown. + * @param item + * The {@link SlimefunItem} responsible. + */ @ParametersAreNonnullByDefault public ErrorReport(T throwable, Location l, SlimefunItem item) { this(throwable, item.getAddon(), stream -> { @@ -91,6 +114,14 @@ public class ErrorReport { }); } + /** + * This constructs a new {@link ErrorReport} for the given {@link SlimefunItem}. + * + * @param throwable + * The {@link Throwable} which caused this {@link ErrorReport}. + * @param item + * The {@link SlimefunItem} responsible. + */ @ParametersAreNonnullByDefault public ErrorReport(T throwable, SlimefunItem item) { this(throwable, item.getAddon(), stream -> { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AsyncMachineProcessCompleteEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AsyncMachineProcessCompleteEvent.java index 02d9412df..872cccf13 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AsyncMachineProcessCompleteEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AsyncMachineProcessCompleteEvent.java @@ -3,6 +3,7 @@ package io.github.thebusybiscuit.slimefun4.api.events; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -26,7 +27,7 @@ public class AsyncMachineProcessCompleteEvent extends Event { private final MachineRecipe machineRecipe; public AsyncMachineProcessCompleteEvent(@Nonnull Location l, @Nullable AContainer container, @Nullable MachineRecipe machineRecipe) { - super(true); + super(!Bukkit.isPrimaryThread()); this.location = l; this.container = container; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoDisenchantEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoDisenchantEvent.java index ad33db977..0a4585470 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoDisenchantEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoDisenchantEvent.java @@ -15,6 +15,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines * * @author poma123 * + * @see AutoEnchantEvent */ public class AutoDisenchantEvent extends Event implements Cancellable { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoEnchantEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoEnchantEvent.java new file mode 100644 index 000000000..04491027b --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/AutoEnchantEvent.java @@ -0,0 +1,63 @@ +package io.github.thebusybiscuit.slimefun4.api.events; + +import javax.annotation.Nonnull; + +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.enchanting.AutoEnchanter; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; + +/** + * An {@link Event} that is called whenever an {@link AutoEnchanter} is trying to enchant + * an {@link ItemStack}. + * + * @author WalshyDev + * + * @see AutoDisenchantEvent + */ +public class AutoEnchantEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final ItemStack item; + private boolean cancelled; + + public AutoEnchantEvent(@Nonnull ItemStack item) { + super(true); + + this.item = item; + } + + /** + * This returns the {@link ItemStack} that is being enchanted. + * + * @return The {@link ItemStack} that is being enchanted + */ + @Nonnull + public ItemStack getItem() { + return item; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Nonnull + public static HandlerList getHandlerList() { + return handlers; + } + + @Nonnull + @Override + public HandlerList getHandlers() { + return getHandlerList(); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java index 81eb79474..c060ed4aa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java @@ -15,10 +15,12 @@ import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; import io.github.thebusybiscuit.cscorelib2.config.Config; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; @@ -47,6 +49,12 @@ public class ResourceManager { private final int[] backgroundSlots = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 17, 18, 26, 27, 35, 36, 44, 45, 46, 48, 49, 50, 52, 53 }; private final Config config; + /** + * This will create a new {@link ResourceManager}. + * + * @param plugin + * Our {@link SlimefunPlugin} instance + */ public ResourceManager(@Nonnull SlimefunPlugin plugin) { config = new Config(plugin, "resources.yml"); } @@ -95,6 +103,7 @@ public class ResourceManager { * * @return An {@link OptionalInt}, either empty or containing the amount of the given {@link GEOResource} */ + @Nonnull public OptionalInt getSupplies(@Nonnull GEOResource resource, @Nonnull World world, int x, int z) { Validate.notNull(resource, "Cannot get supplies for null"); Validate.notNull(world, "World must not be null"); @@ -109,6 +118,20 @@ public class ResourceManager { } } + /** + * This method will set the supplies in a given {@link Chunk} to the specified value. + * + * @param resource + * The {@link GEOResource} + * @param world + * The {@link World} + * @param x + * The x coordinate of that {@link Chunk} + * @param z + * The z coordinate of that {@link Chunk} + * @param value + * The new supply value + */ public void setSupplies(@Nonnull GEOResource resource, @Nonnull World world, int x, int z, int value) { Validate.notNull(resource, "Cannot set supplies for null"); Validate.notNull(world, "World cannot be null"); @@ -117,13 +140,42 @@ public class ResourceManager { BlockStorage.setChunkInfo(world, x, z, key, String.valueOf(value)); } + /** + * This method will generate the default supplies for a given {@link GEOResource} at the + * given {@link Chunk}. + *

+ * This method will invoke {@link #setSupplies(GEOResource, World, int, int, int)} and also calls a + * {@link GEOResourceGenerationEvent}. + * + * @param resource + * The {@link GEOResource} to generate + * @param world + * The {@link World} + * @param x + * The x coordinate of that {@link Chunk} + * @param z + * The z coordinate of that {@link Chunk} + * + * @return The new supply value + */ private int generate(@Nonnull GEOResource resource, @Nonnull World world, int x, int z) { Validate.notNull(resource, "Cannot generate resources for null"); Validate.notNull(world, "World cannot be null"); + // Get the corresponding Block (and Biome) Block block = world.getBlockAt(x << 4, 72, z << 4); - int value = resource.getDefaultSupply(world.getEnvironment(), block.getBiome()); + Biome biome = block.getBiome(); + /* + * getBiome() is marked as NotNull, but it seems like some servers ignore this entirely. + * We have seen multiple reports on Tuinity where it has indeed returned null. + */ + Validate.notNull(biome, "Biome appears to be null for position: " + new BlockPosition(block)); + + // Make sure the value is not below zero. + int value = Math.max(0, resource.getDefaultSupply(world.getEnvironment(), biome)); + + // Check if more than zero units are to be generated. if (value > 0) { int max = resource.getMaxDeviation(); @@ -134,7 +186,8 @@ public class ResourceManager { value += ThreadLocalRandom.current().nextInt(max); } - GEOResourceGenerationEvent event = new GEOResourceGenerationEvent(world, block.getBiome(), x, z, resource, value); + // Fire an event, so that plugins can modify this. + GEOResourceGenerationEvent event = new GEOResourceGenerationEvent(world, biome, x, z, resource, value); Bukkit.getPluginManager().callEvent(event); value = event.getValue(); @@ -166,7 +219,8 @@ public class ResourceManager { int x = block.getX() >> 4; int z = block.getZ() >> 4; - ChestMenu menu = new ChestMenu("&4" + SlimefunPlugin.getLocalization().getResourceString(p, "tooltips.results")); + String title = "&4" + SlimefunPlugin.getLocalization().getResourceString(p, "tooltips.results"); + ChestMenu menu = new ChestMenu(title); for (int slot : backgroundSlots) { menu.addItem(slot, ChestMenuUtils.getBackground(), ChestMenuUtils.getEmptyClickHandler()); 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 d621934ec..7923b7aba 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 @@ -58,6 +58,13 @@ public class GPSNetwork { private final TeleportationManager teleportation = new TeleportationManager(); private final ResourceManager resourceManager; + /** + * This constructs a new {@link GPSNetwork}. + * Note that this network is per {@link Server} and not per {@link Player}. + * + * @param plugin + * Our {@link SlimefunPlugin} instance + */ public GPSNetwork(@Nonnull SlimefunPlugin plugin) { resourceManager = new ResourceManager(plugin); } @@ -125,6 +132,13 @@ public class GPSNetwork { return locations == null ? 0 : locations.size(); } + /** + * This method opens the {@link GPSTransmitter} control panel to the given + * {@link Player}. + * + * @param p + * The {@link Player} + */ public void openTransmitterControlPanel(@Nonnull Player p) { ChestMenu menu = new ChestMenu(ChatColor.BLUE + SlimefunPlugin.getLocalization().getMessage(p, "machines.GPS_CONTROL_PANEL.title")); 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 d1e9407c1..953059db5 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 @@ -7,6 +7,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; import org.bukkit.Location; +import org.bukkit.World.Environment; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -34,53 +35,103 @@ public class Waypoint { private final String name; private final Location location; + /** + * This constructs a new {@link Waypoint} object. + * + * @param profile + * The owning {@link PlayerProfile} + * @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(PlayerProfile profile, String id, Location l, String name) { + public Waypoint(PlayerProfile profile, String id, Location loc, String name) { Validate.notNull(profile, "Profile must never be null!"); Validate.notNull(id, "id must never be null!"); - Validate.notNull(l, "Location must never be null!"); + Validate.notNull(loc, "Location must never be null!"); Validate.notNull(name, "Name must never be null!"); this.profile = profile; this.id = id; - this.location = l; + this.location = loc; this.name = name; } + /** + * This returns the owner of the {@link Waypoint}. + * + * @return The corresponding {@link PlayerProfile} + */ @Nonnull public PlayerProfile getOwner() { return profile; } + /** + * This method returns the unique identifier for this {@link Waypoint}. + * + * @return The {@link Waypoint} id + */ @Nonnull public String getId() { return id; } + /** + * This returns the name of this {@link Waypoint}. + * + * @return The name of this {@link Waypoint} + */ @Nonnull public String getName() { return name; } + /** + * This returns the {@link Location} of this {@link Waypoint} + * + * @return The {@link Waypoint} {@link Location} + */ @Nonnull public Location getLocation() { return location; } + /** + * This method returns whether this {@link Waypoint} is a Deathpoint. + * + * @return Whether this is a Deathpoint + */ public boolean isDeathpoint() { return name.startsWith("player:death "); } + /** + * This method returns the {@link ItemStack} icon for this {@link Waypoint}. + * The icon is dependent on the {@link Environment} the {@link Waypoint} is in + * and whether it is a Deathpoint. + * + * @return The {@link ItemStack} icon for this {@link Waypoint} + */ @Nonnull public ItemStack getIcon() { return SlimefunPlugin.getGPSNetwork().getIcon(name, location.getWorld().getEnvironment()); } + /** + * {@inheritDoc} + */ @Override public int hashCode() { return Objects.hash(profile.getUUID(), id, name, location); } + /** + * {@inheritDoc} + */ @Override public boolean equals(Object obj) { if (!(obj instanceof Waypoint)) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemRestriction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemRestriction.java deleted file mode 100644 index c48a5da31..000000000 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemRestriction.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.thebusybiscuit.slimefun4.api.items; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; - -public interface ItemRestriction { - - /** - * This method represents a check. - * The returned boolean will decide whether to allow the action. - * - * @param profile - * The Player's profile - * @param p - * The Player itself - * @param item - * The SlimefunItem that the {@link ItemStack} represents - * @param itemstack - * The ItemStack that is being tested. - * - * @return Whether the action was allowed - */ - boolean isAllowed(PlayerProfile profile, Player p, SlimefunItem item, ItemStack itemstack); - - /** - * This method is executed if an ItemRestriction took affect. - * Override it to send a message to the Player telling them they cannot - * use that item, or do something else in there. - * - * @param profile - * The Player's profile - * @param p - * The Player to warn - * @param item - * The SlimefunItem that the {@link ItemStack} represents - * @param itemstack - * The ItemStack that was prevented from being used - */ - void warnPlayer(PlayerProfile profile, Player p, SlimefunItem item, ItemStack itemstack); - -} 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 d867ac51a..9b24caca2 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 @@ -471,6 +471,8 @@ public class PlayerProfile { } public boolean hasFullProtectionAgainst(@Nonnull ProtectionType type) { + Validate.notNull(type, "ProtectionType must not be null."); + int armorCount = 0; NamespacedKey setId = null; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java index 9763f6a9a..03ed71260 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java @@ -36,7 +36,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.SurvivalSlimefunGuide; import me.mrCookieSlime.Slimefun.Objects.Category; -import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler; import me.mrCookieSlime.Slimefun.api.BlockInfoConfig; @@ -92,7 +91,6 @@ public final class SlimefunRegistry { private final Map blockMenuPresets = new HashMap<>(); private final Map universalInventories = new HashMap<>(); private final Map, Set> globalItemHandlers = new HashMap<>(); - private final Map blockHandlers = new HashMap<>(); public void load(@Nonnull SlimefunPlugin plugin, @Nonnull Config cfg) { Validate.notNull(plugin, "The Plugin cannot be null!"); @@ -333,12 +331,6 @@ public final class SlimefunRegistry { return globalItemHandlers; } - @Deprecated - @Nonnull - public Map getBlockHandlers() { - return blockHandlers; - } - @Nonnull public Map getWorlds() { return worlds; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java index d64e604b1..804c66a5f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DamageableItem.java @@ -3,7 +3,7 @@ package io.github.thebusybiscuit.slimefun4.core.attributes; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bukkit.Material; +import io.github.thebusybiscuit.slimefun4.utils.UnbreakingAlgorithm; import org.bukkit.Sound; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -22,7 +22,10 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; * option that decides whether or not this {@link SlimefunItem} shall be damageable. * * @author TheBusyBiscuit - * + * @author RobotHanzo + * + * @see UnbreakingAlgorithm + * */ public interface DamageableItem extends ItemAttribute { @@ -47,24 +50,43 @@ public interface DamageableItem extends ItemAttribute { * The {@link ItemStack} to damage */ default void damageItem(@Nonnull Player p, @Nullable ItemStack item) { - if (isDamageable() && item != null && item.getType() != Material.AIR && item.getAmount() > 0) { + if (isDamageable() && item != null && !item.getType().isAir() && item.getAmount() > 0) { int unbreakingLevel = item.getEnchantmentLevel(Enchantment.DURABILITY); - if (unbreakingLevel > 0 && Math.random() * 100 <= (60 + Math.floorDiv(40, (unbreakingLevel + 1)))) { + if (evaluateUnbreakingEnchantment(unbreakingLevel)) { return; } ItemMeta meta = item.getItemMeta(); - Damageable damageable = (Damageable) meta; - if (damageable.getDamage() >= item.getType().getMaxDurability()) { - p.playSound(p.getEyeLocation(), Sound.ENTITY_ITEM_BREAK, 1, 1); - item.setAmount(0); - } else { - damageable.setDamage(damageable.getDamage() + 1); - item.setItemMeta(meta); + if (!meta.isUnbreakable()) { + Damageable damageable = (Damageable) meta; + + if (damageable.getDamage() >= item.getType().getMaxDurability()) { + p.playSound(p.getEyeLocation(), Sound.ENTITY_ITEM_BREAK, 1, 1); + item.setAmount(0); + } else { + damageable.setDamage(damageable.getDamage() + 1); + item.setItemMeta(meta); + } } } } + /** + * This method will randomly decide if the item should be damaged or not + * This does not damage the item, it is called by {@link #damageItem(Player, ItemStack)} to randomly generate a + * boolean + * This function should be overridden when the item type is not a tool which is the default value + * + * @param unbreakingLevel + * The {@link Integer} level of the unbreaking {@link Enchantment} + * + * @return Whether to save the item from taking damage + * + */ + default boolean evaluateUnbreakingEnchantment(int unbreakingLevel) { + return UnbreakingAlgorithm.TOOLS.evaluate(unbreakingLevel); + } + } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java index debf7618f..7b236c095 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.bukkit.Location; import org.bukkit.block.Block; @@ -19,6 +20,7 @@ import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent; import io.github.thebusybiscuit.slimefun4.core.attributes.HologramOwner; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; import me.mrCookieSlime.Slimefun.api.BlockStorage; /** @@ -47,10 +49,12 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { protected final Map roundRobin = new HashMap<>(); private int tickDelayThreshold = 0; + @Nullable public static CargoNet getNetworkFromLocation(@Nonnull Location l) { return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class).orElse(null); } + @Nonnull public static CargoNet getNetworkFromLocationOrCreate(@Nonnull Location l) { Optional cargoNetwork = SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class); @@ -145,7 +149,7 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { } } - public void tick(Block b) { + public void tick(@Nonnull Block b) { if (!regulator.equals(b.getLocation())) { updateHologram(b, "&4Multiple Cargo Regulators connected"); return; @@ -186,7 +190,8 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { } } - private Map mapInputNodes(Set chestTerminalNodes) { + @Nonnull + private Map mapInputNodes(@Nonnull Set chestTerminalNodes) { Map inputs = new HashMap<>(); for (Location node : inputNodes) { @@ -202,7 +207,8 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { return inputs; } - private Map> mapOutputNodes(Set chestTerminalOutputs) { + @Nonnull + private Map> mapOutputNodes(@Nonnull Set chestTerminalOutputs) { Map> output = new HashMap<>(); List list = new LinkedList<>(); @@ -241,7 +247,7 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { /** * This method returns the frequency a given node is set to. - * Should there be an {@link Exception} to this method it will fall back to zero in + * Should there be invalid data this method it will fall back to zero in * order to preserve the integrity of the {@link CargoNet}. * * @param node @@ -249,13 +255,16 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { * * @return The frequency of the given node */ - private static int getFrequency(Location node) { - try { - String str = BlockStorage.getLocationInfo(node).getString("frequency"); - return str == null ? 0 : Integer.parseInt(str); - } catch (Exception x) { - SlimefunPlugin.logger().log(Level.SEVERE, x, () -> "An Error occurred while parsing a Cargo Node Frequency (" + node.getWorld().getName() + " - " + node.getBlockX() + "," + node.getBlockY() + "," + +node.getBlockZ() + ")"); + private static int getFrequency(@Nonnull Location node) { + String frequency = BlockStorage.getLocationInfo(node, "frequency"); + + if (frequency == null) { return 0; + } else if (!PatternUtils.NUMERIC.matcher(frequency).matches()) { + SlimefunPlugin.logger().log(Level.SEVERE, () -> "Failed to parse a Cargo Node Frequency (" + node.getWorld().getName() + " - " + node.getBlockX() + ',' + node.getBlockY() + ',' + node.getBlockZ() + "): " + frequency); + return 0; + } else { + return Integer.parseInt(frequency); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java index aec1b4fd9..662749dc8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNetworkTask.java @@ -1,8 +1,10 @@ package io.github.thebusybiscuit.slimefun4.core.networks.cargo; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; import java.util.Deque; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -158,13 +160,24 @@ class CargoNetworkTask implements Runnable { private ItemStack distributeItem(ItemStack stack, Location inputNode, List outputNodes) { ItemStack item = stack; - Deque destinations = new LinkedList<>(outputNodes); Config cfg = BlockStorage.getLocationInfo(inputNode); boolean roundrobin = Objects.equals(cfg.getString("round-robin"), "true"); boolean smartFill = Objects.equals(cfg.getString("smart-fill"), "true"); + Collection destinations; if (roundrobin) { - roundRobinSort(inputNode, destinations); + // Use an ArrayDeque to perform round-robin sorting + // Since the impl for roundRobinSort just does Deque.addLast(Deque#removeFirst) + // An ArrayDequeue is preferable as opposed to a LinkedList: + // - The number of elements does not change. + // - ArrayDequeue has better iterative performance + Deque tempDestinations = new ArrayDeque<>(outputNodes); + roundRobinSort(inputNode, tempDestinations); + destinations = tempDestinations; + } else { + // Using an ArrayList here since we won't need to sort the destinations + // The ArrayList has the best performance for iteration bar a primitive array + destinations = new ArrayList<>(outputNodes); } for (Location output : destinations) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java index a42d090fb..328126123 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoUtils.java @@ -292,6 +292,11 @@ final class CargoUtils { int maxStackSize = itemInSlot.getType().getMaxStackSize(); int currentAmount = itemInSlot.getAmount(); + if (!smartFill && currentAmount == maxStackSize) { + // Skip full stacks - Performance optimization for non-smartfill nodes + continue; + } + if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false)) { if (currentAmount < maxStackSize) { int amount = currentAmount + stack.getAmount(); @@ -339,11 +344,17 @@ final class CargoUtils { inv.setItem(slot, stack); return null; } else { + int currentAmount = itemInSlot.getAmount(); int maxStackSize = itemInSlot.getType().getMaxStackSize(); + if (!smartFill && currentAmount == maxStackSize) { + // Skip full stacks - Performance optimization for non-smartfill nodes + continue; + } + if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false)) { - if (itemInSlot.getAmount() < maxStackSize) { - int amount = itemInSlot.getAmount() + stack.getAmount(); + if (currentAmount < maxStackSize) { + int amount = currentAmount + stack.getAmount(); if (amount > maxStackSize) { stack.setAmount(amount - maxStackSize); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java index 5577c0a64..6f7206b3a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java @@ -150,7 +150,7 @@ public class LocalizationService extends SlimefunLocalization { @Override public Language getLanguage(@Nonnull Player p) { - Validate.notNull("Player cannot be null!"); + Validate.notNull(p, "Player cannot be null!"); PersistentDataContainer container = p.getPersistentDataContainer(); String language = container.get(languageKey, PersistentDataType.STRING); @@ -253,7 +253,7 @@ public class LocalizationService extends SlimefunLocalization { } @Nonnull - private Set getKeys(FileConfiguration... files) { + private Set getKeys(@Nonnull FileConfiguration... files) { Set keys = new HashSet<>(); for (FileConfiguration cfg : files) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java index 34deb9f2c..789d4ce19 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java @@ -50,8 +50,11 @@ class ContributionsConnector extends GitHubConnector { * These people are... "special cases". */ private void loadConfiguration() { + // Bots and invalid accounts we want to ignore. ignoredAccounts.add("invalid-email-address"); + ignoredAccounts.add("renovate"); ignoredAccounts.add("renovate-bot"); + ignoredAccounts.add("renovate[bot]"); ignoredAccounts.add("TheBusyBot"); ignoredAccounts.add("ImgBotApp"); ignoredAccounts.add("imgbot"); @@ -61,6 +64,7 @@ class ContributionsConnector extends GitHubConnector { ignoredAccounts.add("gitlocalize-app[bot]"); ignoredAccounts.add("mt-gitlocalize"); + // Known Minecraft aliases. aliases.put("WalshyDev", "HumanRightsAct"); aliases.put("J3fftw1", "_lagpc_"); aliases.put("ajan-12", "ajan_12"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java index 06dc4da27..a20ef5a07 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java @@ -13,6 +13,8 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.apache.commons.lang.Validate; + import io.github.thebusybiscuit.cscorelib2.config.Config; import io.github.thebusybiscuit.slimefun4.core.services.localization.Translators; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; @@ -78,10 +80,14 @@ public class GitHubService { * the usual methods. */ private void addDefaultContributors() { + // Artists addContributor("Fuffles_", "&dArtist"); - addContributor("IMS_Art", "&dArtist"); + addContributor("IMS_Art", "https://github.com/IAmSorryArt", "&dArtist", 0); + + // Addon Jam winners addContributor("nahkd123", "&aWinner of the 2020 Addon Jam"); + // Translators new Translators(this); } @@ -94,6 +100,11 @@ public class GitHubService { @Nonnull public Contributor addContributor(@Nonnull String minecraftName, @Nonnull String profileURL, @Nonnull String role, int commits) { + Validate.notNull(minecraftName, "Minecraft username must not be null."); + Validate.notNull(profileURL, "GitHub profile url must not be null."); + Validate.notNull(role, "Role should not be null."); + Validate.isTrue(commits >= 0, "Commit count cannot be negative.");; + String username = profileURL.substring(profileURL.lastIndexOf('/') + 1); Contributor contributor = contributors.computeIfAbsent(username, key -> new Contributor(minecraftName, profileURL)); @@ -106,9 +117,10 @@ public class GitHubService { this.logging = logging; addDefaultContributors(); - // TheBusyBiscuit/Slimefun4 (twice because there may me multiple pages) + // TheBusyBiscuit/Slimefun4 (multiple times because there may me multiple pages) connectors.add(new ContributionsConnector(this, "code", 1, repository, "developer")); connectors.add(new ContributionsConnector(this, "code2", 2, repository, "developer")); + connectors.add(new ContributionsConnector(this, "code3", 3, repository, "developer")); // TheBusyBiscuit/Slimefun4-Wiki connectors.add(new ContributionsConnector(this, "wiki", 1, "Slimefun/Wiki", "wiki")); @@ -122,6 +134,7 @@ public class GitHubService { this.pendingPullRequests = pullRequests; })); + // Forks, star count and last commit date connectors.add(new GitHubActivityConnector(this, repository, (forks, stars, date) -> { this.publicForks = forks; this.stargazers = stars; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java index 533b184ec..0e4460c3d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Language.java @@ -160,8 +160,7 @@ public final class Language { */ @Nonnull public String getName(@Nonnull Player p) { - String name = SlimefunPlugin.getLocalization().getMessage(p, "languages." + id); - return name != null ? name : toString(); + return SlimefunPlugin.getLocalization().getMessage(p, "languages." + id); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java index 02f00f8f8..5943f2a57 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java @@ -1,13 +1,15 @@ package io.github.thebusybiscuit.slimefun4.core.services.localization; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.UnaryOperator; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import org.apache.commons.lang.Validate; import org.bukkit.ChatColor; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; @@ -33,9 +35,9 @@ import net.md_5.bungee.api.chat.TextComponent; /** * This is an abstract parent class of {@link LocalizationService}. * There is not really much more I can say besides that... - * + * * @author TheBusyBiscuit - * + * * @see LocalizationService * */ @@ -48,10 +50,10 @@ public abstract class SlimefunLocalization extends Localization implements Keyed /** * This method attempts to return the {@link Language} with the given * language code. - * + * * @param id * The language code - * + * * @return A {@link Language} with the given id or null */ @Nullable @@ -59,28 +61,30 @@ public abstract class SlimefunLocalization extends Localization implements Keyed /** * This method returns the currently selected {@link Language} of a {@link Player}. - * + * * @param p * The {@link Player} to query - * + * * @return The {@link Language} that was selected by the given {@link Player} */ + @Nullable public abstract Language getLanguage(@Nonnull Player p); /** * This method returns the default {@link Language} of this {@link Server} - * + * * @return The default {@link Language} */ + @Nullable public abstract Language getDefaultLanguage(); /** * This returns whether a {@link Language} with the given id exists within * the project resources. - * + * * @param id * The {@link Language} id - * + * * @return Whether the project contains a {@link Language} with that id */ protected abstract boolean hasLanguage(@Nonnull String id); @@ -88,7 +92,7 @@ public abstract class SlimefunLocalization extends Localization implements Keyed /** * This method returns a full {@link Collection} of every {@link Language} that was * found. - * + * * @return A {@link Collection} that contains every installed {@link Language} */ @Nonnull @@ -96,7 +100,7 @@ public abstract class SlimefunLocalization extends Localization implements Keyed /** * This method adds a new {@link Language} with the given id and texture. - * + * * @param id * The {@link Language} id * @param texture @@ -117,7 +121,28 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } } - public String getMessage(Player p, String key) { + @Nonnull + @Override + public String getMessage(@Nonnull String key) { + Validate.notNull(key, "Message key must not be null!"); + + Language language = getDefaultLanguage(); + + String message = language == null ? null : language.getMessagesFile().getString(key); + + if (message == null) { + Language fallback = getLanguage(SupportedLanguage.ENGLISH.getLanguageId()); + return fallback.getMessagesFile().getString(key); + } + + return message; + } + + @Nonnull + public String getMessage(@Nonnull Player p, @Nonnull String key) { + Validate.notNull(p, "Player must not be null!"); + Validate.notNull(key, "Message key must not be null!"); + Language language = getLanguage(p); if (language == null) { @@ -134,11 +159,15 @@ public abstract class SlimefunLocalization extends Localization implements Keyed return message; } - public List getMessages(Player p, String key) { + @Nonnull + public List getMessages(@Nonnull Player p, @Nonnull String key) { + Validate.notNull(p, "Player should not be null."); + Validate.notNull(key, "Message key cannot be null."); + Language language = getLanguage(p); if (language == null) { - return Arrays.asList("NO LANGUAGE FOUND"); + return Collections.singletonList("NO LANGUAGE FOUND"); } List messages = language.getMessagesFile().getStringList(key); @@ -151,37 +180,55 @@ public abstract class SlimefunLocalization extends Localization implements Keyed return messages; } + @Nonnull + @ParametersAreNonnullByDefault public List getMessages(Player p, String key, UnaryOperator function) { + Validate.notNull(p, "Player cannot be null."); + Validate.notNull(key, "Message key cannot be null."); + Validate.notNull(function, "Function cannot be null."); + List messages = getMessages(p, key); messages.replaceAll(function); return messages; } - public String getResearchName(Player p, NamespacedKey key) { + @Nullable + public String getResearchName(@Nonnull Player p, @Nonnull NamespacedKey key) { + Validate.notNull(p, "Player must not be null."); + Validate.notNull(key, "NamespacedKey cannot be null."); + Language language = getLanguage(p); - if (language.getResearchesFile() == null) { + if (language == null || language.getResearchesFile() == null) { return null; } - return language.getResearchesFile().getString(key.getNamespace() + "." + key.getKey()); + return language.getResearchesFile().getString(key.getNamespace() + '.' + key.getKey()); } - public String getCategoryName(Player p, NamespacedKey key) { + @Nullable + public String getCategoryName(@Nonnull Player p, @Nonnull NamespacedKey key) { + Validate.notNull(p, "Player must not be null."); + Validate.notNull(key, "NamespacedKey cannot be null!"); + Language language = getLanguage(p); - if (language.getCategoriesFile() == null) { + if (language == null || language.getCategoriesFile() == null) { return null; } - return language.getCategoriesFile().getString(key.getNamespace() + "." + key.getKey()); + return language.getCategoriesFile().getString(key.getNamespace() + '.' + key.getKey()); } - public String getResourceString(Player p, String key) { + @Nullable + public String getResourceString(@Nonnull Player p, @Nonnull String key) { + Validate.notNull(p, "Player should not be null!"); + Validate.notNull(key, "Message key should not be null!"); + Language language = getLanguage(p); - String value = language.getResourcesFile() != null ? language.getResourcesFile().getString(key) : null; + String value = language != null && language.getResourcesFile() != null ? language.getResourcesFile().getString(key) : null; if (value != null) { return value; @@ -191,16 +238,20 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } } - public ItemStack getRecipeTypeItem(Player p, RecipeType recipeType) { + @Nullable + public ItemStack getRecipeTypeItem(@Nonnull Player p, @Nonnull RecipeType recipeType) { + Validate.notNull(p, "Player cannot be null!"); + Validate.notNull(recipeType, "Recipe type cannot be null!"); + Language language = getLanguage(p); ItemStack item = recipeType.toItem(); NamespacedKey key = recipeType.getKey(); - if (language.getRecipeTypesFile() == null || !language.getRecipeTypesFile().contains(key.getNamespace() + "." + key.getKey())) { + if (language == null || language.getRecipeTypesFile() == null || !language.getRecipeTypesFile().contains(key.getNamespace() + '.' + key.getKey())) { language = getLanguage("en"); } - if (!language.getRecipeTypesFile().contains(key.getNamespace() + "." + key.getKey())) { + if (!language.getRecipeTypesFile().contains(key.getNamespace() + '.' + key.getKey())) { return item; } @@ -218,7 +269,10 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } @Override - public void sendMessage(CommandSender recipient, String key, boolean addPrefix) { + public void sendMessage(@Nonnull CommandSender recipient, @Nonnull String key, boolean addPrefix) { + Validate.notNull(recipient, "Recipient cannot be null!"); + Validate.notNull(key, "Message key cannot be null!"); + String prefix = addPrefix ? getPrefix() : ""; if (recipient instanceof Player) { @@ -229,6 +283,9 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } public void sendActionbarMessage(@Nonnull Player player, @Nonnull String key, boolean addPrefix) { + Validate.notNull(player, "Player cannot be null!"); + Validate.notNull(key, "Message key cannot be null!"); + String prefix = addPrefix ? getPrefix() : ""; String message = ChatColors.color(prefix + getMessage(player, key)); @@ -237,15 +294,17 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } @Override - public void sendMessage(CommandSender recipient, String key) { + public void sendMessage(@Nonnull CommandSender recipient, @Nonnull String key) { sendMessage(recipient, key, true); } + @ParametersAreNonnullByDefault public void sendMessage(CommandSender recipient, String key, UnaryOperator function) { sendMessage(recipient, key, true, function); } @Override + @ParametersAreNonnullByDefault public void sendMessage(CommandSender recipient, String key, boolean addPrefix, UnaryOperator function) { if (SlimefunPlugin.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { return; @@ -261,7 +320,7 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } @Override - public void sendMessages(CommandSender recipient, String key) { + public void sendMessages(@Nonnull CommandSender recipient, @Nonnull String key) { String prefix = getPrefix(); if (recipient instanceof Player) { @@ -278,6 +337,7 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } @Override + @ParametersAreNonnullByDefault public void sendMessages(CommandSender recipient, String key, boolean addPrefix, UnaryOperator function) { String prefix = addPrefix ? getPrefix() : ""; @@ -294,8 +354,8 @@ public abstract class SlimefunLocalization extends Localization implements Keyed } } + @ParametersAreNonnullByDefault public void sendMessages(CommandSender recipient, String key, UnaryOperator function) { sendMessages(recipient, key, true, function); } - } 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 081002518..70e43ddf0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java @@ -404,7 +404,7 @@ public final class SlimefunItems { public static final SlimefunItemStack PURE_ORE_CLUSTER = new SlimefunItemStack("PURE_ORE_CLUSTER", Material.GUNPOWDER, "&6Pure Ore Cluster"); public static final SlimefunItemStack SMALL_URANIUM = new SlimefunItemStack("SMALL_URANIUM", HeadTexture.URANIUM, "&cSmall Chunk of Uranium", "", LoreBuilder.radioactive(Radioactivity.MODERATE), LoreBuilder.HAZMAT_SUIT_REQUIRED); public static final SlimefunItemStack TINY_URANIUM = new SlimefunItemStack("TINY_URANIUM", HeadTexture.URANIUM, "&cTiny Pile of Uranium", "", LoreBuilder.radioactive(Radioactivity.LOW)); - public static final SlimefunItemStack SOLAR_PANEL = new SlimefunItemStack("SOLAR_PANEL", Material.DAYLIGHT_DETECTOR, "&bSolar Panel", "", "&a&oTransforms Sunlight to Energy"); + public static final SlimefunItemStack SOLAR_PANEL = new SlimefunItemStack("SOLAR_PANEL", Material.DAYLIGHT_DETECTOR, "&9Photovoltaic Cell", "", "&7Important component for", "&7crafting a &bSolar Generator"); public static final SlimefunItemStack PLASTIC_SHEET = new SlimefunItemStack("PLASTIC_SHEET", Material.PAPER, "&fPlastic Sheet"); public static final SlimefunItemStack MAGNET = new SlimefunItemStack("MAGNET", HeadTexture.MAGNET, "&cMagnet"); @@ -415,7 +415,7 @@ public final class SlimefunItems { public static final SlimefunItemStack HEATING_COIL = new SlimefunItemStack("HEATING_COIL", HeadTexture.HEATING_COIL, "&cHeating Coil"); public static final SlimefunItemStack COOLING_UNIT = new SlimefunItemStack("COOLING_UNIT", HeadTexture.COOLING_UNIT, "&bCooling Unit"); public static final SlimefunItemStack ELECTRIC_MOTOR = new SlimefunItemStack("ELECTRIC_MOTOR", HeadTexture.MOTOR, "&cElectric Motor"); - public static final SlimefunItemStack CARGO_MOTOR = new SlimefunItemStack("CARGO_MOTOR", HeadTexture.MOTOR, "&3Cargo Motor"); + public static final SlimefunItemStack CARGO_MOTOR = new SlimefunItemStack("CARGO_MOTOR", HeadTexture.CARGO_MOTOR, "&3Cargo Motor", "", "&7Important ingredient for items", "&7related to Cargo Management"); public static final SlimefunItemStack SCROLL_OF_DIMENSIONAL_TELEPOSITION = new SlimefunItemStack("SCROLL_OF_DIMENSIONAL_TELEPOSITION", Material.PAPER, "&6Scroll of Dimensional Teleposition", "", "&cThis Scroll is capable of creating", "&ca temporary black Hole which pulls", "&cnearby Entities into itself and sends", "&cthem into another Dimension where", "&ceverything is turned around", "", "&fIn other words: Makes Entities turn by 180 Degrees"); public static final SlimefunItemStack TOME_OF_KNOWLEDGE_SHARING = new SlimefunItemStack("TOME_OF_KNOWLEDGE_SHARING", Material.ENCHANTED_BOOK, "&6Tome of Knowledge Sharing", "&7Owner: &bNone", "", "&eRight Click&7 to bind this Tome to yourself", "", "", "&eRight Click&7 to obtain all Researches by", "&7the previously assigned Owner"); public static final SlimefunItemStack HARDENED_GLASS = new SlimefunItemStack("HARDENED_GLASS", Material.LIGHT_GRAY_STAINED_GLASS, "&7Hardened Glass", "", "&fWithstands Explosions"); @@ -425,6 +425,7 @@ public final class SlimefunItems { public static final SlimefunItemStack ANCIENT_PEDESTAL = new SlimefunItemStack("ANCIENT_PEDESTAL", Material.DISPENSER, "&dAncient Pedestal", "", "&5Part of the Ancient Altar"); public static final SlimefunItemStack ANCIENT_ALTAR = new SlimefunItemStack("ANCIENT_ALTAR", Material.ENCHANTING_TABLE, "&dAncient Altar", "", "&5Multi-Block Altar for", "&5magical Crafting Processes"); public static final SlimefunItemStack COPPER_WIRE = new SlimefunItemStack("COPPER_WIRE", Material.STRING, "&6Copper Wire", "", "&6Crucial component in electric modules"); + public static final SlimefunItemStack CRAFTING_MOTOR = new SlimefunItemStack("CRAFTING_MOTOR", HeadTexture.CRAFTING_MOTOR, "&6Crafting Motor", "", "&7Important component of Auto-Crafters"); /* Rainbow blocks */ private static final String RAINBOW = "&dCycles through all Colors of the Rainbow!"; @@ -782,7 +783,9 @@ public final class SlimefunItems { public static final SlimefunItemStack CARGO_OUTPUT_NODE = new SlimefunItemStack("CARGO_NODE_OUTPUT", HeadTexture.CARGO_OUTPUT_NODE, "&7Cargo Node &c(Output)", "", "&fCargo Output Pipe"); public static final SlimefunItemStack CARGO_OUTPUT_NODE_2 = new SlimefunItemStack("CARGO_NODE_OUTPUT_ADVANCED", HeadTexture.CARGO_OUTPUT_NODE, "&6Advanced Cargo Node &c(Output)", "", "&fCargo Output Pipe"); + // Animal farm public static final SlimefunItemStack AUTO_BREEDER = new SlimefunItemStack("AUTO_BREEDER", Material.HAY_BLOCK, "&eAuto-Breeder", "", "&fRuns on &aOrganic Food", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.powerBuffer(1024), "&8\u21E8 &e\u26A1 &760 J/Animal"); + public static final SlimefunItemStack PRODUCE_COLLECTOR = new SlimefunItemStack("PRODUCE_COLLECTOR", Material.HAY_BLOCK, "&bProduce Collector", "", "&fThis machine allows you to", "&fcollect produce from nearby animals.", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), LoreBuilder.powerBuffer(512), LoreBuilder.powerPerSecond(32)); public static final SlimefunItemStack ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD", HeadTexture.FILLED_CAN, "&aOrganic Food", "&7Content: &9???"); public static final SlimefunItemStack WHEAT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_WHEAT", HeadTexture.FILLED_CAN, ORGANIC_FOOD.getDisplayName(), "&7Content: &9Wheat"); @@ -849,6 +852,7 @@ public final class SlimefunItems { public static final SlimefunItemStack VANILLA_AUTO_CRAFTER = new SlimefunItemStack("VANILLA_AUTO_CRAFTER", HeadTexture.VANILLA_AUTO_CRAFTER, "&2Auto-Crafter &8(Vanilla)", "", "&fPlace this machine on top of a", "&fchest or similar and make it craft", "&fanything that can be crafted using a", "&fnormal &eCrafting Table", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), "&8\u21E8 &e\u26A1 &716 J/Item"); public static final SlimefunItemStack ENHANCED_AUTO_CRAFTER = new SlimefunItemStack("ENHANCED_AUTO_CRAFTER", HeadTexture.ENHANCED_AUTO_CRAFTER, "&2Auto-Crafter &8(Enhanced)", "", "&fPlace this machine on top of a", "&fchest or similar and make it craft", "&fanything that can be crafted using an", "&eEnhanced Crafting Table", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), "&8\u21E8 &e\u26A1 &716 J/Item"); + public static final SlimefunItemStack ARMOR_AUTO_CRAFTER = new SlimefunItemStack("ARMOR_AUTO_CRAFTER", HeadTexture.ARMOR_AUTO_CRAFTER, "&2Auto-Crafter &8(Armor Forge)", "", "&fPlace this machine on top of a", "&fchest or similar and make it craft", "&fanything that can be crafted using an", "&eArmor Forge", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.MACHINE), "&8\u21E8 &e\u26A1 &732 J/Item"); public static final SlimefunItemStack IRON_GOLEM_ASSEMBLER = new SlimefunItemStack("IRON_GOLEM_ASSEMBLER", Material.IRON_BLOCK, "&6Iron Golem Assembler", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), "&8\u21E8 &7Cooldown: &b30 Seconds", LoreBuilder.powerBuffer(4096), "&8\u21E8 &e\u26A1 &72048 J/Golem"); public static final SlimefunItemStack WITHER_ASSEMBLER = new SlimefunItemStack("WITHER_ASSEMBLER", Material.OBSIDIAN, "&5Wither Assembler", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), "&8\u21E8 &7Cooldown: &b30 Seconds", LoreBuilder.powerBuffer(4096), "&8\u21E8 &e\u26A1 &74096 J/Wither"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java index e0cf42aef..c76cdaa4d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java @@ -34,6 +34,7 @@ import io.github.thebusybiscuit.cscorelib2.protection.ProtectionManager; import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.exceptions.TagMisconfigurationException; +import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource; import io.github.thebusybiscuit.slimefun4.api.gps.GPSNetwork; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.core.SlimefunRegistry; @@ -778,6 +779,13 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { return instance.items; } + /** + * This returns our {@link GPSNetwork} instance. + * The {@link GPSNetwork} is responsible for handling any GPS-related + * operations and for managing any {@link GEOResource}. + * + * @return Our {@link GPSNetwork} instance + */ @Nonnull public static GPSNetwork getGPSNetwork() { validateInstance(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java index 4f1ffef80..9bf0df3fa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java @@ -153,7 +153,7 @@ public enum Instruction { * ahead of them. */ ATTACK_MOBS(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> { - Predicate predicate = e -> e instanceof Monster; + Predicate predicate = Monster.class::isInstance; android.attack(b, face, predicate); }), @@ -162,7 +162,7 @@ public enum Instruction { * ahead of them. */ ATTACK_ANIMALS(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> { - Predicate predicate = e -> e instanceof Animals; + Predicate predicate = Animals.class::isInstance; android.attack(b, face, predicate); }), diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java index 806384d2d..08a4ad41f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java @@ -24,6 +24,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Rotatable; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; @@ -36,6 +37,7 @@ import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; @@ -52,7 +54,6 @@ import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; @@ -132,23 +133,7 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock, } }; - // TODO: Move this into a BlockBreakHandler - registerBlockHandler(getId(), (p, b, stack, reason) -> { - boolean allow = reason == UnregisterReason.PLAYER_BREAK && (BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(p.getUniqueId().toString()) || p.hasPermission("slimefun.android.bypass")); - - if (allow) { - BlockMenu inv = BlockStorage.getInventory(b); - - if (inv != null) { - inv.dropItems(b.getLocation(), 43); - inv.dropItems(b.getLocation(), getOutputSlots()); - } - } - - return allow; - }); - - addItemHandler(onPlace()); + addItemHandler(onPlace(), onBreak()); } @Nonnull @@ -178,6 +163,31 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock, }; } + @Nonnull + private BlockBreakHandler onBreak() { + return new BlockBreakHandler(false, false) { + + @Override + public void onPlayerBreak(BlockBreakEvent e, ItemStack item, List drops) { + Block b = e.getBlock(); + String owner = BlockStorage.getLocationInfo(b.getLocation(), "owner"); + + if (!e.getPlayer().hasPermission("slimefun.android.bypass") && !e.getPlayer().getUniqueId().toString().equals(owner)) { + // The Player is not allowed to break this android + e.setCancelled(true); + return; + } + + BlockMenu inv = BlockStorage.getInventory(b); + + if (inv != null) { + inv.dropItems(b.getLocation(), 43); + inv.dropItems(b.getLocation(), getOutputSlots()); + } + } + }; + } + /** * This returns the {@link AndroidType} that is associated with this {@link ProgrammableAndroid}. * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/ElytraCap.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/ElytraCap.java index 5e9df87ac..483d84f3b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/ElytraCap.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/ElytraCap.java @@ -1,8 +1,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.armor; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import io.github.thebusybiscuit.slimefun4.utils.UnbreakingAlgorithm; import org.bukkit.GameMode; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; @@ -41,12 +43,17 @@ public class ElytraCap extends SlimefunArmorPiece implements DamageableItem, Pro } @Override - public void damageItem(Player p, ItemStack item) { + public void damageItem(@Nonnull Player p, @Nullable ItemStack item) { if (p.getGameMode() != GameMode.CREATIVE) { DamageableItem.super.damageItem(p, item); } } + @Override + public boolean evaluateUnbreakingEnchantment(int unbreakingLevel) { + return UnbreakingAlgorithm.ARMOR.evaluate(unbreakingLevel); + } + @Nonnull @Override public ProtectionType[] getProtectionTypes() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java index 2dea241be..6d476cc13 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java @@ -1,6 +1,8 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Predicate; @@ -9,7 +11,6 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; -import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -27,6 +28,7 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.data.PersistentDataAPI; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; @@ -35,6 +37,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.listeners.AutoCrafterListener; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AsyncRecipeChoiceTask; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; +import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.papermc.lib.PaperLib; import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult; @@ -73,6 +76,11 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy */ protected final NamespacedKey recipeStorageKey; + /** + * The {@link NamespacedKey} used to determine whether the recipe is enabled. + */ + protected final NamespacedKey recipeEnabledKey; + // @formatter:off protected final int[] background = { 0, 1, 2, 3, 4, 5, 6, 7, 8, @@ -84,10 +92,11 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy // @formatter:on @ParametersAreNonnullByDefault - public AbstractAutoCrafter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + protected AbstractAutoCrafter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); recipeStorageKey = new NamespacedKey(SlimefunPlugin.instance(), "recipe_key"); + recipeEnabledKey = new NamespacedKey(SlimefunPlugin.instance(), "recipe_enabled"); addItemHandler(new BlockTicker() { @@ -142,19 +151,28 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy } } + /** + * This method performs one tick for the {@link AbstractAutoCrafter}. + * + * @param b + * The block for this {@link AbstractAutoCrafter} + * @param data + * The data stored on this block + */ protected void tick(@Nonnull Block b, @Nonnull Config data) { AbstractRecipe recipe = getSelectedRecipe(b); - if (recipe == null || getCharge(b.getLocation(), data) < getEnergyConsumption()) { - // No valid recipe selected, abort... + if (recipe == null || !recipe.isEnabled() || getCharge(b.getLocation(), data) < getEnergyConsumption()) { + // No recipe / disabled recipe / no energy, abort... return; } - Block chest = b.getRelative(BlockFace.DOWN); + // The block below where we would expect our inventory holder. + Block targetBlock = b.getRelative(BlockFace.DOWN); // Make sure this is a Chest - if (isValidInventory(chest)) { - BlockState state = PaperLib.getBlockState(chest, false).getState(); + if (isValidInventory(targetBlock)) { + BlockState state = PaperLib.getBlockState(targetBlock, false).getState(); if (state instanceof InventoryHolder) { Inventory inv = ((InventoryHolder) state).getInventory(); @@ -236,6 +254,9 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy if (recipe == null) { // Clear the value from persistent data storage PersistentDataAPI.remove((Skull) state, recipeStorageKey); + + // Also remove the "enabled" state since this should be per-recipe. + PersistentDataAPI.remove((Skull) state, recipeEnabledKey); } else { // Store the value to persistent data storage PersistentDataAPI.setString((Skull) state, recipeStorageKey, recipe.toString()); @@ -271,14 +292,29 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy ChestMenuUtils.drawBackground(menu, background); ChestMenuUtils.drawBackground(menu, 45, 46, 47, 48, 50, 51, 52, 53); - menu.addItem(49, new CustomItem(Material.BARRIER, ChatColor.RED + SlimefunPlugin.getLocalization().getMessage(p, "messages.auto-crafting.remove"))); - menu.addMenuClickHandler(49, (pl, item, slot, action) -> { - setSelectedRecipe(b, null); - pl.closeInventory(); - p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); - SlimefunPlugin.getLocalization().sendMessage(p, "messages.auto-crafting.recipe-removed"); - return false; - }); + if (recipe.isEnabled()) { + menu.addItem(49, new CustomItem(Material.BARRIER, SlimefunPlugin.getLocalization().getMessages(p, "messages.auto-crafting.tooltips.enabled"))); + menu.addMenuClickHandler(49, (pl, item, slot, action) -> { + if (action.isRightClicked()) { + deleteRecipe(pl, b); + } else { + setRecipeEnabled(pl, b, false); + } + + return false; + }); + } else { + menu.addItem(49, new CustomItem(HeadTexture.EXCLAMATION_MARK.getAsItemStack(), SlimefunPlugin.getLocalization().getMessages(p, "messages.auto-crafting.tooltips.disabled"))); + menu.addMenuClickHandler(49, (pl, item, slot, action) -> { + if (action.isRightClicked()) { + deleteRecipe(pl, b); + } else { + setRecipeEnabled(pl, b, true); + } + + return false; + }); + } // This makes the slots cycle through different ingredients AsyncRecipeChoiceTask task = new AsyncRecipeChoiceTask(); @@ -293,6 +329,32 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy } } + @ParametersAreNonnullByDefault + private void setRecipeEnabled(Player p, Block b, boolean enabled) { + p.closeInventory(); + p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + BlockState state = PaperLib.getBlockState(b, false).getState(); + + // Make sure the block is still a Skull + if (state instanceof Skull) { + if (enabled) { + PersistentDataAPI.remove((Skull) state, recipeEnabledKey); + SlimefunPlugin.getLocalization().sendMessage(p, "messages.auto-crafting.re-enabled"); + } else { + PersistentDataAPI.setByte((Skull) state, recipeEnabledKey, (byte) 1); + SlimefunPlugin.getLocalization().sendMessage(p, "messages.auto-crafting.temporarily-disabled"); + } + } + } + + @ParametersAreNonnullByDefault + private void deleteRecipe(Player p, Block b) { + setSelectedRecipe(b, null); + p.closeInventory(); + p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 1, 1); + SlimefunPlugin.getLocalization().sendMessage(p, "messages.auto-crafting.recipe-removed"); + } + /** * This method checks whether the given {@link Predicate} matches the provided {@link ItemStack}. * @@ -347,6 +409,11 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy Validate.notNull(inv, "The Inventory must not be null"); Validate.notNull(recipe, "The Recipe shall not be null"); + // Make sure that the Recipe is actually enabled + if (!recipe.isEnabled()) { + return false; + } + // Check if we have an empty slot if (inv.firstEmpty() != -1) { Map itemQuantities = new HashMap<>(); @@ -358,23 +425,79 @@ public abstract class AbstractAutoCrafter extends SlimefunItem implements Energy } } + List leftoverItems = new ArrayList<>(); + // Remove ingredients for (Map.Entry entry : itemQuantities.entrySet()) { ItemStack item = inv.getItem(entry.getKey()); // Double-check to be extra safe if (item != null) { + // Handle leftovers + ItemStack leftover = getLeftoverItem(item); + + if (leftover != null) { + // Account for the amount of removed items + leftover.setAmount(item.getAmount() - entry.getValue()); + leftoverItems.add(leftover); + } + + // Update the item amount item.setAmount(entry.getValue()); } } - // All Predicates have found a match - return inv.addItem(recipe.getResult().clone()).isEmpty(); + boolean success = inv.addItem(recipe.getResult().clone()).isEmpty(); + + if (success) { + // Fixes #2926 - Push leftover items to the inventory. + for (ItemStack leftoverItem : leftoverItems) { + inv.addItem(leftoverItem); + } + } + + return success; } return false; } + /** + * This method returns the "leftovers" from a crafting operation. + * The method functions very similarly to {@link Material#getCraftingRemainingItem()}. + * However we cannot use this method as it is only available in the latest 1.16 snapshots + * of Spigot, not even on earlier 1.16 builds... + * But this gives us more control over the leftovers anyway! + * + * @param item + * The {@link ItemStack} that is being consumed + * + * @return The leftover item or null if the item is fully consumed + */ + @Nullable + private ItemStack getLeftoverItem(@Nonnull ItemStack item) { + Material type = item.getType(); + + switch (type) { + case WATER_BUCKET: + case LAVA_BUCKET: + case MILK_BUCKET: + return new ItemStack(Material.BUCKET); + case DRAGON_BREATH: + case POTION: + return new ItemStack(Material.GLASS_BOTTLE); + default: + MinecraftVersion minecraftVersion = SlimefunPlugin.getMinecraftVersion(); + + // Honey does not exist in 1.14 + if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15) && type == Material.HONEY_BOTTLE) { + return new ItemStack(Material.GLASS_BOTTLE); + } else { + return null; + } + } + } + /** * This method returns the max amount of electricity this machine can hold. * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractRecipe.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractRecipe.java index 1a8b23f01..ad0c4c081 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractRecipe.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractRecipe.java @@ -44,6 +44,11 @@ public abstract class AbstractRecipe { */ private final ItemStack result; + /** + * Whether this recipe is enabled. + */ + private boolean enabled = true; + /** * Protected constructor. For implementation classes only. * @@ -83,6 +88,27 @@ public abstract class AbstractRecipe { return result; } + /** + * This returns whether or not this recipe has been enabled. + * A disabled recipe will not be crafted. + * + * @return Whether this recipe is enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * This method enables or disables this recipe. + * A disabled recipe will not be crafted. + * + * @param enabled + * Whether this recipe is enabled + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + /** * This will visually represent this {@link AbstractRecipe} in the given {@link ChestMenu}. * Any {@link MaterialChoice} will be cycled through using the {@link AsyncRecipeChoiceTask}. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java new file mode 100644 index 000000000..29034bc1b --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java @@ -0,0 +1,31 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.ArmorForge; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.Category; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; + +/** + * The {@link ArmorAutoCrafter} is an implementation of the {@link AbstractAutoCrafter}. + * It can craft items that are crafted using the {@link ArmorForge}. + * + * @author TheBusyBiscuit + * + * @see ArmorForge + * @see AbstractAutoCrafter + * @see SlimefunAutoCrafter + * @see SlimefunItemRecipe + * + */ +public class ArmorAutoCrafter extends SlimefunAutoCrafter { + + @ParametersAreNonnullByDefault + public ArmorAutoCrafter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(category, item, recipeType, recipe, RecipeType.ARMOR_FORGE); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java index 0a7cfb83b..d42fd1b9e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java @@ -13,8 +13,9 @@ import org.bukkit.block.BlockState; import org.bukkit.block.Skull; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; -import io.github.thebusybiscuit.cscorelib2.data.PersistentDataAPI; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AsyncRecipeChoiceTask; @@ -60,11 +61,15 @@ public class SlimefunAutoCrafter extends AbstractAutoCrafter { if (state instanceof Skull) { // Read the stored value from persistent data storage - String value = PersistentDataAPI.getString((Skull) state, recipeStorageKey); + PersistentDataContainer container = ((Skull) state).getPersistentDataContainer(); + String value = container.get(recipeStorageKey, PersistentDataType.STRING); SlimefunItem item = SlimefunItem.getByID(value); if (item != null) { - return AbstractRecipe.of(item, targetRecipeType); + boolean enabled = !container.has(recipeEnabledKey, PersistentDataType.BYTE); + AbstractRecipe recipe = AbstractRecipe.of(item, targetRecipeType); + recipe.setEnabled(enabled); + return recipe; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java index 4004bbe19..fc1e132a3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java @@ -9,7 +9,6 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -22,9 +21,11 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapelessRecipe; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; -import io.github.thebusybiscuit.cscorelib2.data.PersistentDataAPI; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; +import io.github.thebusybiscuit.slimefun4.core.services.MinecraftRecipeService; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AsyncRecipeChoiceTask; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; @@ -58,16 +59,12 @@ public class VanillaAutoCrafter extends AbstractAutoCrafter { @Override @Nullable public AbstractRecipe getSelectedRecipe(@Nonnull Block b) { - return AbstractRecipe.of(getRecipe(b)); - } - - @Nullable - private Recipe getRecipe(@Nonnull Block b) { BlockState state = PaperLib.getBlockState(b, false).getState(); if (state instanceof Skull) { // Read the stored value from persistent data storage - String value = PersistentDataAPI.getString((Skull) state, recipeStorageKey); + PersistentDataContainer container = ((Skull) state).getPersistentDataContainer(); + String value = container.get(recipeStorageKey, PersistentDataType.STRING); if (value != null) { String[] values = PatternUtils.COLON.split(value); @@ -79,8 +76,15 @@ public class VanillaAutoCrafter extends AbstractAutoCrafter { */ @SuppressWarnings("deprecation") NamespacedKey key = new NamespacedKey(values[0], values[1]); + Recipe keyedRecipe = SlimefunPlugin.getMinecraftRecipeService().getRecipe(key); - return SlimefunPlugin.getMinecraftRecipeService().getRecipe(key); + if (keyedRecipe != null) { + boolean enabled = !container.has(recipeEnabledKey, PersistentDataType.BYTE); + AbstractRecipe recipe = AbstractRecipe.of(keyedRecipe); + recipe.setEnabled(enabled); + + return recipe; + } } } @@ -169,7 +173,10 @@ public class VanillaAutoCrafter extends AbstractAutoCrafter { private List getRecipesFor(@Nonnull ItemStack item) { List recipes = new ArrayList<>(); - for (Recipe recipe : Bukkit.getRecipesFor(item)) { + // Fixes #2913 - Bukkit.getRecipesFor() only checks for Materials + MinecraftRecipeService recipeService = SlimefunPlugin.getMinecraftRecipeService(); + + for (Recipe recipe : recipeService.getRecipesFor(item)) { if (recipe instanceof ShapedRecipe || recipe instanceof ShapelessRecipe) { recipes.add(recipe); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java index 3bc22d700..181fbdf39 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java @@ -13,6 +13,7 @@ import org.bukkit.Material; import org.bukkit.Nameable; import org.bukkit.OfflinePlayer; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.Dispenser; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; @@ -97,16 +98,7 @@ public class BlockPlacer extends SlimefunItem { e.setCancelled(true); - if (!material.isBlock() || SlimefunTag.BLOCK_PLACER_IGNORED_MATERIALS.isTagged(material)) { - /* - * Some materials cannot be reliably placed, like beds, - * it would look kinda wonky, so we just ignore these altogether. - * The event has already been cancelled too, so they won't drop. - */ - return; - } - - if (facedBlock.isEmpty() && isAllowed(material) && dispenser.getInventory().getViewers().isEmpty()) { + if (facedBlock.isEmpty() && dispenser.getInventory().getViewers().isEmpty() && isAllowed(facedBlock, material)) { SlimefunItem item = SlimefunItem.getByItem(e.getItem()); if (item != null) { @@ -158,14 +150,33 @@ public class BlockPlacer extends SlimefunItem { * * @return Whether placing this {@link Material} is allowed */ - private boolean isAllowed(@Nonnull Material type) { - for (String blockType : unplaceableBlocks.getValue()) { - if (type.toString().equals(blockType)) { - return false; + private boolean isAllowed(@Nonnull Block facedBlock, @Nonnull Material type) { + if (!type.isBlock()) { + // Make sure the material is actually a block. + return false; + } else if (type == Material.CAKE) { + /* + * Special case for cakes. + * Cakes are a lie but I really want the Block Placer to place them down!!! + */ + return !facedBlock.getRelative(BlockFace.DOWN).isPassable(); + } else if (SlimefunTag.BLOCK_PLACER_IGNORED_MATERIALS.isTagged(type)) { + /* + * Some materials cannot be reliably placed, like beds, + * it would look kinda wonky, so we just ignore these altogether. + * The event has already been cancelled too, so they won't drop. + */ + return false; + } else { + // Check for all unplaceable block + for (String blockType : unplaceableBlocks.getValue()) { + if (type.toString().equals(blockType)) { + return false; + } } - } - return true; + return true; + } } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java index 0e741cfb9..512205530 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java @@ -188,7 +188,8 @@ public class Crucible extends SimpleSlimefunItem implements Rec private void placeLiquid(@Nonnull Block block, boolean water) { if (block.getType() == Material.AIR || block.getType() == Material.CAVE_AIR || block.getType() == Material.VOID_AIR) { - block.setType(water ? Material.WATER : Material.LAVA); + // Fixes #2903 - Cancel physics update to resolve weird overlapping + block.setType(water ? Material.WATER : Material.LAVA, false); } else { if (water && block.getBlockData() instanceof Waterlogged) { Waterlogged wl = (Waterlogged) block.getBlockData(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java index 3825f133e..d6d2e2b3a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.inventory.ItemStack; @@ -31,7 +32,7 @@ public class UnplaceableBlock extends SimpleSlimefunItem impleme } @ParametersAreNonnullByDefault - public UnplaceableBlock(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { + public UnplaceableBlock(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, @Nullable ItemStack recipeOutput) { super(category, item, recipeType, recipe, recipeOutput); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDrier.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDrier.java index cea222eae..0322df01b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDrier.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDrier.java @@ -3,6 +3,8 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine import java.util.ArrayList; import java.util.List; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.inventory.ItemStack; @@ -27,6 +29,7 @@ public class AutoDrier extends AContainer implements RecipeDisplayItem, NotHoppe private List recipeList; + @ParametersAreNonnullByDefault public AutoDrier(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutomatedCraftingChamber.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutomatedCraftingChamber.java index 69295f738..502e3253d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutomatedCraftingChamber.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutomatedCraftingChamber.java @@ -3,9 +3,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.ParametersAreNonnullByDefault; @@ -21,21 +19,18 @@ import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable; -import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; +import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.AbstractAutoCrafter; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; -import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer; -import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer.ItemFlag; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; -import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; @@ -44,10 +39,9 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; /** - * This class needs to be rewritten VERY BADLY. - * But we should focus on rewriting the recipe system first. + * This machine is disabled and has been replaced by Auto Crafters. * - * @deprecated This is horribly done. Someone needs to rewrite this. + * @deprecated This has been replaced by the {@link AbstractAutoCrafter}. * * @author TheBusyBiscuit * @@ -58,13 +52,11 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I private final int[] inputBorder = { 9, 10, 11, 12, 13, 18, 22, 27, 31, 36, 40, 45, 46, 47, 48, 49 }; private final int[] outputBorder = { 23, 24, 25, 26, 32, 35, 41, 42, 43, 44 }; - private final Map craftingRecipes = new HashMap<>(); - @ParametersAreNonnullByDefault public AutomatedCraftingChamber(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); - new BlockMenuPreset(getId(), "&4Deprecated item. Do not use.") { + new BlockMenuPreset(getId(), "&4Machine is disabled.") { @Override public void init() { @@ -88,17 +80,11 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I return false; }); } - - menu.replaceExistingItem(7, new CustomItem(Material.CRAFTING_TABLE, "&7Craft Last", "", "&e> Click to craft the last shaped recipe", "&cOnly works with the last one")); - menu.addMenuClickHandler(7, (p, slot, item, action) -> { - tick(b, true); - return false; - }); } @Override public boolean canOpen(Block b, Player p) { - p.sendMessage(ChatColor.DARK_RED + "This item has been deprecated. It will be removed soon!"); + p.sendMessage(ChatColor.DARK_RED + "This item has been disabled and will be removed soon. Please switch over to the new Auto Crafters in the Cargo Category."); return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @@ -132,17 +118,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I } }; - addItemHandler(onPlace()); - registerBlockHandler(getId(), (p, b, stack, reason) -> { - BlockMenu inv = BlockStorage.getInventory(b); - - if (inv != null) { - inv.dropItems(b.getLocation(), getInputSlots()); - inv.dropItems(b.getLocation(), getOutputSlots()); - } - - return true; - }); + addItemHandler(onPlace(), onBreak()); } private BlockPlaceHandler onPlace() { @@ -150,7 +126,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I @Override public void onPlayerPlace(BlockPlaceEvent e) { - e.getPlayer().sendMessage(ChatColor.DARK_RED + "This item has been deprecated. It will be removed soon!"); + e.getPlayer().sendMessage(ChatColor.DARK_RED + "This item has been disabled and will be removed soon. Please switch over to the new Auto Crafters in the Cargo Category."); BlockStorage.addBlockInfo(e.getBlock(), "enabled", String.valueOf(false)); } @@ -161,6 +137,21 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I }; } + private BlockBreakHandler onBreak() { + return new SimpleBlockBreakHandler() { + + @Override + public void onBlockBreak(Block b) { + BlockMenu inv = BlockStorage.getInventory(b); + + if (inv != null) { + inv.dropItems(b.getLocation(), getInputSlots()); + inv.dropItems(b.getLocation(), getOutputSlots()); + } + } + }; + } + private Comparator compareSlots(DirtyChestMenu menu) { return (slot1, slot2) -> menu.getItemInSlot(slot1).getAmount() - menu.getItemInSlot(slot2).getAmount(); } @@ -212,106 +203,4 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I public EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.CONSUMER; } - - @Override - public void preRegister() { - addItemHandler(new BlockTicker() { - - @Override - public void tick(Block b, SlimefunItem sf, Config data) { - AutomatedCraftingChamber.this.tick(b, false); - } - - @Override - public boolean isSynchronized() { - return false; - } - }); - } - - protected void tick(Block block, boolean craftLast) { - if (!craftLast && BlockStorage.getLocationInfo(block.getLocation(), "enabled").equals(String.valueOf(false))) { - return; - } - - if (getCharge(block.getLocation()) < getEnergyConsumption()) { - return; - } - - String input = getSerializedInput(block, craftLast); - testInputAgainstRecipes(block, input); - } - - private String getSerializedInput(Block block, boolean craftLast) { - BlockMenu menu = BlockStorage.getInventory(block); - StringBuilder builder = new StringBuilder(); - int i = 0; - boolean lastIteration = false; - - for (int j = 0; j < 9; j++) { - if (i > 0) { - builder.append(" "); - } - - ItemStack item = menu.getItemInSlot(getInputSlots()[j]); - - if (item != null && item.getAmount() == 1) { - if (craftLast) { - lastIteration = true; - } else { - return ""; - } - } - - builder.append(CustomItemSerializer.serialize(item, ItemFlag.MATERIAL, ItemFlag.ITEMMETA_DISPLAY_NAME, ItemFlag.ITEMMETA_LORE)); - - i++; - } - - // we're only executing the last possible shaped recipe - // we don't want to allow this to be pressed instead of the default timer-based - // execution to prevent abuse and auto clickers - if (craftLast && !lastIteration) { - return ""; - } - - return builder.toString(); - } - - private void testInputAgainstRecipes(Block block, String input) { - BlockMenu menu = BlockStorage.getInventory(block); - - ItemStack output = craftingRecipes.get(input); - if (output != null && menu.fits(output, getOutputSlots())) { - menu.pushItem(output.clone(), getOutputSlots()); - removeCharge(block.getLocation(), getEnergyConsumption()); - - for (int j = 0; j < 9; j++) { - if (menu.getItemInSlot(getInputSlots()[j]) != null) { - menu.consumeItem(getInputSlots()[j]); - } - } - } - } - - public void loadRecipes() { - EnhancedCraftingTable machine = (EnhancedCraftingTable) SlimefunItems.ENHANCED_CRAFTING_TABLE.getItem(); - - for (ItemStack[] inputs : RecipeType.getRecipeInputList(machine)) { - StringBuilder builder = new StringBuilder(); - int i = 0; - - for (ItemStack item : inputs) { - if (i > 0) { - builder.append(" "); - } - - builder.append(CustomItemSerializer.serialize(item, ItemFlag.MATERIAL, ItemFlag.ITEMMETA_DISPLAY_NAME, ItemFlag.ITEMMETA_LORE)); - - i++; - } - - craftingRecipes.put(builder.toString(), RecipeType.getRecipeOutputList(machine, inputs)); - } - } } \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/CarbonPress.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/CarbonPress.java index d6403c373..3bcd27a97 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/CarbonPress.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/CarbonPress.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.inventory.ItemStack; @@ -13,6 +15,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; public class CarbonPress extends AContainer implements RecipeDisplayItem { + @ParametersAreNonnullByDefault public CarbonPress(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java index 403085a55..5c00354db 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java @@ -6,8 +6,10 @@ import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; +import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; @@ -34,6 +36,7 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem { addRecipe(5, new ItemStack(Material.GLASS), new ItemStack(Material.GLASS_PANE, 3)); addRecipe(4, new ItemStack(Material.SNOWBALL, 4), new ItemStack(Material.SNOW_BLOCK)); addRecipe(4, new ItemStack(Material.MAGMA_CREAM, 4), new ItemStack(Material.MAGMA_BLOCK)); + addRecipe(4, new ItemStack(Material.SLIME_BALL, 9), new ItemStack(Material.SLIME_BLOCK)); addRecipe(3, new ItemStack(Material.DRIED_KELP, 9), new ItemStack(Material.DRIED_KELP_BLOCK)); addRecipe(3, new ItemStack(Material.BONE_MEAL, 9), new ItemStack(Material.BONE_BLOCK)); @@ -70,6 +73,10 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem { addRecipe(8, new ItemStack(Material.EMERALD, 9), new ItemStack(Material.EMERALD_BLOCK)); addRecipe(8, new ItemStack(Material.DIAMOND, 9), new ItemStack(Material.DIAMOND_BLOCK)); + + if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { + addRecipe(16, new ItemStack(Material.NETHERITE_INGOT, 9), new ItemStack(Material.NETHERITE_BLOCK)); + } } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/FluidPump.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/FluidPump.java index aa6f67e54..7d44e99e7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/FluidPump.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/FluidPump.java @@ -186,13 +186,15 @@ public class FluidPump extends SimpleSlimefunItem implements Invent @Nonnull private ItemStack getFilledBucket(@Nonnull Block fluid) { - if (fluid.getType() == Material.LAVA) { - return new ItemStack(Material.LAVA_BUCKET); - } else if (fluid.getType() == Material.WATER || fluid.getType() == Material.BUBBLE_COLUMN) { - return new ItemStack(Material.WATER_BUCKET); - } else { - // Fallback for any new liquids - return new ItemStack(Material.BUCKET); + switch (fluid.getType()) { + case LAVA: + return new ItemStack(Material.LAVA_BUCKET); + case WATER: + case BUBBLE_COLUMN: + return new ItemStack(Material.WATER_BUCKET); + default: + // Fallback for any new liquids + return new ItemStack(Material.BUCKET); } } @@ -214,6 +216,7 @@ public class FluidPump extends SimpleSlimefunItem implements Invent return levelled.getLevel() == 0; } } + return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/Freezer.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/Freezer.java index 009d89471..d0bd7b616 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/Freezer.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/Freezer.java @@ -28,6 +28,8 @@ public class Freezer extends AContainer implements RecipeDisplayItem { registerRecipe(6, new ItemStack[] { new ItemStack(Material.PACKED_ICE) }, new ItemStack[] { new ItemStack(Material.BLUE_ICE) }); registerRecipe(8, new ItemStack[] { new ItemStack(Material.BLUE_ICE) }, new ItemStack[] { SlimefunItems.REACTOR_COOLANT_CELL }); registerRecipe(6, new ItemStack[] { new ItemStack(Material.SNOW_BLOCK, 2) }, new ItemStack[] { new ItemStack(Material.ICE) }); + registerRecipe(6, new ItemStack[] { new ItemStack(Material.MAGMA_CREAM) }, new ItemStack[] { new ItemStack(Material.SLIME_BALL) }); + registerRecipe(6, new ItemStack[] { new ItemStack(Material.MAGMA_BLOCK, 2) }, new ItemStack[] { new ItemStack(Material.SLIME_BLOCK) }); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/enchanting/BookBinder.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/enchanting/BookBinder.java index 16d2550e3..0036d75b0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/enchanting/BookBinder.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/enchanting/BookBinder.java @@ -110,48 +110,25 @@ public class BookBinder extends AContainer { @ParametersAreNonnullByDefault private Map combineEnchantments(Map ech1, Map ech2) { Map enchantments = new HashMap<>(); - boolean conflicts = false; enchantments.putAll(ech1); + boolean hasConflicts = false; for (Map.Entry entry : ech2.entrySet()) { for (Map.Entry conflictsWith : enchantments.entrySet()) { - // Check if entry enchantment and conflictsWith enchantment conflict, and confirm that the enchantsments - // aren't the exact same. + /* + * Check if entry enchantment and conflictsWith enchantment conflict + * and confirm that the enchantsments aren't the exact same. + */ if (entry.getKey().conflictsWith(conflictsWith.getKey()) && !entry.getKey().equals(conflictsWith.getKey())) { - conflicts = true; + hasConflicts = true; } } - if (!conflicts) { + if (!hasConflicts) { enchantments.merge(entry.getKey(), entry.getValue(), (a, b) -> { - int enchantMaxLevel = entry.getKey().getMaxLevel(); - - if (a.intValue() == b.intValue()) { - - // Confirm the entry's enchant level doesn't go over the maximum unless it uses - // bypass-vanilla-max-level - if (enchantMaxLevel <= a && !bypassVanillaMaxLevel.getValue()) { - return enchantMaxLevel; - } else if (hasCustomMaxLevel.getValue()) { - return a + 1 > customMaxLevel.getValue() ? customMaxLevel.getValue() : a + 1; - } else { - return a + 1; - } - } else { - int highestLevel = Math.max(a, b); - - // Confirm the entry's enchant level doesn't go over the maximum unless it uses - // bypass-vanilla-max-level - if (enchantMaxLevel <= highestLevel && !bypassVanillaMaxLevel.getValue()) { - return enchantMaxLevel; - } else if (hasCustomMaxLevel.getValue()) { - return highestLevel > customMaxLevel.getValue() ? customMaxLevel.getValue() : highestLevel; - } else { - return highestLevel; - } - - } + int maxLevel = entry.getKey().getMaxLevel(); + return combineEnchantmentLevels(maxLevel, a, b); }); } } @@ -159,4 +136,35 @@ public class BookBinder extends AContainer { return enchantments; } + + private int combineEnchantmentLevels(int maxLevel, int lvl1, int lvl2) { + if (lvl1 == lvl2) { + /* + * Confirm the entry's enchant level doesn't go over the maximum + * unless it uses bypass-vanilla-max-level + */ + if (maxLevel <= lvl1 && !bypassVanillaMaxLevel.getValue()) { + return maxLevel; + } else if (hasCustomMaxLevel.getValue()) { + return lvl1 + 1 > customMaxLevel.getValue() ? customMaxLevel.getValue() : lvl1 + 1; + } else { + return lvl1 + 1; + } + } else { + int highestLevel = Math.max(lvl1, lvl2); + + /* + * Confirm the entry's enchant level doesn't go over the maximum + * unless it uses bypass-vanilla-max-level + */ + if (maxLevel <= highestLevel && !bypassVanillaMaxLevel.getValue()) { + return maxLevel; + } else if (hasCustomMaxLevel.getValue()) { + return highestLevel > customMaxLevel.getValue() ? customMaxLevel.getValue() : highestLevel; + } else { + return highestLevel; + } + + } + } } \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java index 1f887237f..b6d35d54d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java @@ -1,11 +1,17 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Effect; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; @@ -14,6 +20,7 @@ import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; @@ -25,7 +32,6 @@ import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -59,7 +65,8 @@ public abstract class AbstractEntityAssembler extends SimpleSl private int lifetime = 0; - public AbstractEntityAssembler(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + @ParametersAreNonnullByDefault + protected AbstractEntityAssembler(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); new BlockMenuPreset(getId(), item.getImmutableMeta().getDisplayName().orElse("Entity Assembler")) { @@ -108,23 +115,10 @@ public abstract class AbstractEntityAssembler extends SimpleSl } }; - addItemHandler(onPlace()); - registerBlockHandler(getId(), (p, b, stack, reason) -> { - if (reason == UnregisterReason.EXPLODE) { - return false; - } - - BlockMenu inv = BlockStorage.getInventory(b); - - if (inv != null) { - inv.dropItems(b.getLocation(), headSlots); - inv.dropItems(b.getLocation(), bodySlots); - } - - return true; - }); + addItemHandler(onPlace(), onBreak()); } + @Nonnull private BlockPlaceHandler onPlace() { return new BlockPlaceHandler(true) { @@ -145,6 +139,23 @@ public abstract class AbstractEntityAssembler extends SimpleSl }; } + @Nonnull + private BlockBreakHandler onBreak() { + return new BlockBreakHandler(false, false) { + + @Override + public void onPlayerBreak(BlockBreakEvent e, ItemStack item, List drops) { + Block b = e.getBlock(); + BlockMenu inv = BlockStorage.getInventory(b); + + if (inv != null) { + inv.dropItems(b.getLocation(), headSlots); + inv.dropItems(b.getLocation(), bodySlots); + } + } + }; + } + private void updateBlockInventory(BlockMenu menu, Block b) { if (!BlockStorage.hasBlockInfo(b) || BlockStorage.getLocationInfo(b.getLocation(), KEY_ENABLED) == null || BlockStorage.getLocationInfo(b.getLocation(), KEY_ENABLED).equals(String.valueOf(false))) { menu.replaceExistingItem(22, new CustomItem(Material.GUNPOWDER, "&7Enabled: &4\u2718", "", "&e> Click to enable this Machine")); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AnimalProduce.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AnimalProduce.java new file mode 100644 index 000000000..ed4e2c381 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AnimalProduce.java @@ -0,0 +1,39 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities; + +import java.util.function.Predicate; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; + +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe; + +/** + * An {@link AnimalProduce} can be obtained via the {@link ProduceCollector}. + * + * @author TheBusyBiscuit + * + * @see ProduceCollector + * + */ +public class AnimalProduce extends MachineRecipe implements Predicate { + + private final Predicate predicate; + + @ParametersAreNonnullByDefault + public AnimalProduce(ItemStack input, ItemStack result, Predicate predicate) { + super(5, new ItemStack[] { input }, new ItemStack[] { result }); + Validate.notNull(predicate, "The Predicate must not be null"); + + this.predicate = predicate; + } + + @Override + public boolean test(@Nonnull LivingEntity entity) { + return predicate.test(entity); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AutoBreeder.java similarity index 99% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AutoBreeder.java index d76591338..558c0feed 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AutoBreeder.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; +package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ProduceCollector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ProduceCollector.java new file mode 100644 index 000000000..82bf8d40a --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ProduceCollector.java @@ -0,0 +1,164 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.MushroomCow; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils; +import io.github.thebusybiscuit.cscorelib2.item.CustomItem; +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; +import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting; +import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.Category; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe; +import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; + +/** + * The {@link ProduceCollector} allows you to collect produce from animals. + * Providing it with a bucket and a nearby {@link Cow} will allow you to obtain milk. + * + * @author TheBusyBiscuit + * + */ +public class ProduceCollector extends AContainer implements RecipeDisplayItem { + + private final ItemSetting range = new IntRangeSetting(this, "range", 1, 2, 32); + private final Set animalProduces = new HashSet<>(); + + @ParametersAreNonnullByDefault + public ProduceCollector(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(category, item, recipeType, recipe); + + addItemSetting(range); + } + + @Override + protected void registerDefaultRecipes() { + // Milk from adult cows + addProduce(new AnimalProduce(new ItemStack(Material.BUCKET), new ItemStack(Material.MILK_BUCKET), n -> { + if (n instanceof Cow) { + return ((Cow) n).isAdult(); + } else { + return false; + } + })); + + // Mushroom Stew from Mooshrooms + addProduce(new AnimalProduce(new ItemStack(Material.BOWL), new ItemStack(Material.MUSHROOM_STEW), n -> { + if (n instanceof MushroomCow) { + return ((MushroomCow) n).isAdult(); + } else { + return false; + } + })); + } + + /** + * This method adds a new {@link AnimalProduce} to this machine. + * + * @param produce + * The {@link AnimalProduce} to add + */ + public void addProduce(@Nonnull AnimalProduce produce) { + Validate.notNull(produce, "A produce cannot be null"); + + this.animalProduces.add(produce); + } + + @Override + public void preRegister() { + addItemHandler(new BlockTicker() { + + @Override + public void tick(Block b, SlimefunItem sf, Config data) { + ProduceCollector.this.tick(b); + } + + @Override + public boolean isSynchronized() { + // We override the preRegister() method to override the sync setting here + return true; + } + }); + } + + @Override + public List getDisplayRecipes() { + List displayRecipes = new ArrayList<>(); + + displayRecipes.add(new CustomItem(Material.BUCKET, null, "&fRequires &bCow &fnearby")); + displayRecipes.add(new ItemStack(Material.MILK_BUCKET)); + + displayRecipes.add(new CustomItem(Material.BOWL, null, "&fRequires &bMooshroom &fnearby")); + displayRecipes.add(new ItemStack(Material.MUSHROOM_STEW)); + + return displayRecipes; + } + + @Override + protected MachineRecipe findNextRecipe(@Nonnull BlockMenu inv) { + for (int slot : getInputSlots()) { + for (AnimalProduce produce : animalProduces) { + ItemStack item = inv.getItemInSlot(slot); + + if (!SlimefunUtils.isItemSimilar(item, produce.getInput()[0], true) || !InvUtils.fits(inv.toInventory(), produce.getOutput()[0], getOutputSlots())) { + continue; + } + + if (isAnimalNearby(inv.getBlock(), produce::test)) { + inv.consumeItem(slot); + return produce; + } + } + } + + return null; + } + + @ParametersAreNonnullByDefault + private boolean isAnimalNearby(Block b, Predicate predicate) { + int radius = range.getValue(); + return !b.getWorld().getNearbyEntities(b.getLocation(), radius, radius, radius, n -> isValidAnimal(n, predicate)).isEmpty(); + } + + @ParametersAreNonnullByDefault + private boolean isValidAnimal(Entity n, Predicate predicate) { + if (n instanceof LivingEntity) { + return predicate.test((LivingEntity) n); + } else { + return false; + } + } + + @Override + public String getMachineIdentifier() { + return "PRODUCE_COLLECTOR"; + } + + @Override + public ItemStack getProgressBar() { + return new ItemStack(Material.SHEARS); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/XPCollector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/XPCollector.java deleted file mode 100644 index 83778f07f..000000000 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/XPCollector.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities; - -import java.util.Iterator; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ExperienceOrb; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.inventory.ItemStack; - -import io.github.thebusybiscuit.cscorelib2.item.CustomItem; -import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; -import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; -import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; -import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; -import me.mrCookieSlime.Slimefun.Lists.RecipeType; -import me.mrCookieSlime.Slimefun.Objects.Category; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; -import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; -import me.mrCookieSlime.Slimefun.api.BlockStorage; -import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; - -public class XPCollector extends SlimefunItem implements InventoryBlock, EnergyNetComponent { - - private final int[] border = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; - - private static final int ENERGY_CONSUMPTION = 10; - private static final String DATA_KEY = "stored-exp"; - - public XPCollector(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { - super(category, item, recipeType, recipe); - - createPreset(this, this::constructMenu); - - addItemHandler(onPlace()); - registerBlockHandler(getId(), (p, b, stack, reason) -> { - BlockMenu inv = BlockStorage.getInventory(b); - - if (inv != null) { - inv.dropItems(b.getLocation(), getOutputSlots()); - } - - return true; - }); - } - - private BlockPlaceHandler onPlace() { - return new BlockPlaceHandler(false) { - - @Override - public void onPlayerPlace(BlockPlaceEvent e) { - BlockStorage.addBlockInfo(e.getBlock(), "owner", e.getPlayer().getUniqueId().toString()); - } - }; - } - - @Override - public int[] getInputSlots() { - return new int[0]; - } - - @Override - public int[] getOutputSlots() { - return new int[] { 12, 13, 14 }; - } - - @Override - public EnergyNetComponentType getEnergyComponentType() { - return EnergyNetComponentType.CONSUMER; - } - - @Override - public int getCapacity() { - return 1024; - } - - protected void constructMenu(BlockMenuPreset preset) { - for (int slot : border) { - preset.addItem(slot, new CustomItem(Material.PURPLE_STAINED_GLASS_PANE, " "), (p, s, item, action) -> false); - } - } - - @Override - public void preRegister() { - addItemHandler(new BlockTicker() { - - @Override - public void tick(Block b, SlimefunItem sf, Config data) { - XPCollector.this.tick(b); - } - - @Override - public boolean isSynchronized() { - return true; - } - }); - } - - protected void tick(Block b) { - Iterator iterator = b.getWorld().getNearbyEntities(b.getLocation(), 4.0, 4.0, 4.0, n -> n instanceof ExperienceOrb && n.isValid()).iterator(); - int experiencePoints = 0; - - while (iterator.hasNext() && experiencePoints == 0) { - Entity entity = iterator.next(); - - if (getCharge(b.getLocation()) < ENERGY_CONSUMPTION) { - return; - } - - experiencePoints = getStoredExperience(b) + ((ExperienceOrb) entity).getExperience(); - - removeCharge(b.getLocation(), ENERGY_CONSUMPTION); - entity.remove(); - - int withdrawn = 0; - BlockMenu menu = BlockStorage.getInventory(b); - - for (int level = 0; level < getStoredExperience(b); level = level + 10) { - if (menu.fits(SlimefunItems.FILLED_FLASK_OF_KNOWLEDGE, getOutputSlots())) { - withdrawn = withdrawn + 10; - menu.pushItem(SlimefunItems.FILLED_FLASK_OF_KNOWLEDGE.clone(), getOutputSlots()); - } - } - - BlockStorage.addBlockInfo(b, DATA_KEY, String.valueOf(experiencePoints - withdrawn)); - } - } - - private int getStoredExperience(Block b) { - Config cfg = BlockStorage.getLocationInfo(b.getLocation()); - String value = cfg.getString(DATA_KEY); - - if (value != null) { - return Integer.parseInt(value); - } else { - BlockStorage.addBlockInfo(b, DATA_KEY, "0"); - return 0; - } - } - -} 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 84aa14c10..6fe5ec5ab 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 @@ -53,7 +53,7 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO public GEOMiner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); - addItemHandler(onPlace(), onBreak()); + addItemHandler(onPlace()); } @Nonnull @@ -68,7 +68,8 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO } @Nonnull - private BlockBreakHandler onBreak() { + @Override + protected BlockBreakHandler onBlockBreak() { return new SimpleBlockBreakHandler() { @Override @@ -87,26 +88,31 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO }; } + @Nonnull @Override public String getMachineIdentifier() { return "GEO_MINER"; } + @Nonnull @Override public ItemStack getProgressBar() { return new ItemStack(Material.DIAMOND_PICKAXE); } + @Nonnull @Override public int[] getInputSlots() { return new int[0]; } + @Nonnull @Override public int[] getOutputSlots() { return OUTPUT_SLOTS; } + @Nonnull @Override public List getDisplayRecipes() { List displayRecipes = new LinkedList<>(); @@ -120,13 +126,14 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO return displayRecipes; } + @Nonnull @Override public String getLabelLocalPath() { return "guide.tooltips.recipes.miner"; } @Override - protected void constructMenu(BlockMenuPreset preset) { + protected void constructMenu(@Nonnull BlockMenuPreset preset) { for (int i : BORDER) { preset.addItem(i, new CustomItem(Material.GRAY_STAINED_GLASS_PANE, " "), (p, slot, item, action) -> false); } @@ -154,7 +161,7 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO } @Override - protected void tick(Block b) { + protected void tick(@Nonnull Block b) { BlockMenu inv = BlockStorage.getInventory(b); if (isProcessing(b)) { @@ -183,7 +190,7 @@ public class GEOMiner extends AContainer implements RecipeDisplayItem, HologramO } } - private void start(Block b, BlockMenu inv) { + private void start(@Nonnull Block b, @Nonnull BlockMenu inv) { for (GEOResource resource : SlimefunPlugin.getRegistry().getGEOResources().values()) { if (resource.isObtainableFromGEOMiner()) { OptionalInt optional = SlimefunPlugin.getGPSNetwork().getResourceManager().getSupplies(resource, b.getWorld(), b.getX() >> 4, b.getZ() >> 4); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/GoldIngot.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/GoldIngot.java new file mode 100644 index 000000000..69200feb6 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/GoldIngot.java @@ -0,0 +1,60 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items.misc; + +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang.Validate; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Smeltery; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.Category; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; + +/** + * The {@link GoldIngot} from Slimefun is a simple resource which is divided into different + * levels of carat ratings. + *

+ * It can be obtained via gold dust and other gold ingots in a {@link Smeltery}. + * + * @author TheBusyBiscuit + * + * @see Smeltery + * + */ +public class GoldIngot extends SlimefunItem { + + /** + * The carat rating. + */ + private final int caratRating; + + @ParametersAreNonnullByDefault + public GoldIngot(Category category, int caratRating, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(category, item, recipeType, recipe); + + Validate.isTrue(caratRating > 0, "Carat rating must be above zero."); + Validate.isTrue(caratRating <= 24, "Carat rating cannot go above 24."); + this.caratRating = caratRating; + } + + /** + * This returns the carat rating of this {@link GoldIngot}. + *

+ * The purity of the {@link GoldIngot} is measured in carat (1-24). + * + *

+     * 24k = 100% gold.
+     * 18k = 75% gold.
+     * 12k = 50% gold.
+     * 
+ * + * and so on... + * + * @return The carat rating of this {@link GoldIngot} + */ + public int getCaratRating() { + return caratRating; + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java index 7d66099fc..1b328545f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java @@ -51,7 +51,11 @@ public class ArmorForge extends AbstractCraftingTable { } } - SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + if (inv.isEmpty()) { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.inventory-empty", true); + } else { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java index 5aeab8fb5..bb30cab7f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java @@ -50,7 +50,11 @@ public class EnhancedCraftingTable extends AbstractCraftingTable { } } - SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + if (inv.isEmpty()) { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.inventory-empty", true); + } else { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/GrindStone.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/GrindStone.java index 54d28c391..9950dbb46 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/GrindStone.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/GrindStone.java @@ -76,6 +76,9 @@ public class GrindStone extends MultiBlockMachine { recipes.add(new ItemStack(Material.NETHER_WART_BLOCK)); recipes.add(new ItemStack(Material.NETHER_WART, 9)); + + recipes.add(new ItemStack(Material.QUARTZ_BLOCK)); + recipes.add(new ItemStack(Material.QUARTZ, 4)); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/MagicWorkbench.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/MagicWorkbench.java index d98935eb1..7fe4e5b15 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/MagicWorkbench.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/MagicWorkbench.java @@ -57,7 +57,11 @@ public class MagicWorkbench extends AbstractCraftingTable { } } - SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + if (inv.isEmpty()) { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.inventory-empty", true); + } else { + SlimefunPlugin.getLocalization().sendMessage(p, "machines.pattern-not-found", true); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreCrusher.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreCrusher.java index 162f05625..874c07ad3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreCrusher.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreCrusher.java @@ -89,7 +89,7 @@ public class OreCrusher extends MultiBlockMachine { recipes.add(new ItemStack(Material.GRAVEL)); recipes.add(new ItemStack(Material.SAND)); - recipes.add(new ItemStack(Material.MAGMA_BLOCK, 4)); + recipes.add(new ItemStack(Material.MAGMA_BLOCK)); recipes.add(SlimefunItems.SULFATE); recipes.add(SlimefunItems.CARBON); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ClimbingPick.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ClimbingPick.java index 9f6a7122f..ddc485819 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ClimbingPick.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ClimbingPick.java @@ -11,6 +11,7 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; @@ -235,7 +236,7 @@ public class ClimbingPick extends SimpleSlimefunItem implements } @Override - public void damageItem(Player p, ItemStack item) { + public void damageItem(@Nonnull Player p, @Nullable ItemStack item) { if (p.getGameMode() != GameMode.CREATIVE) { DamageableItem.super.damageItem(p, item); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ExplosiveTool.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ExplosiveTool.java index 4bb51e450..ff06089f4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ExplosiveTool.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/ExplosiveTool.java @@ -12,6 +12,7 @@ import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.inventory.ItemStack; @@ -20,15 +21,14 @@ import io.github.thebusybiscuit.slimefun4.api.events.ExplosiveToolBreakBlocksEve import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; -import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -64,12 +64,12 @@ public class ExplosiveTool extends SimpleSlimefunItem implements b.getWorld().playSound(b.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 0.2F, 1F); List blocks = findBlocks(b); - breakBlocks(p, tool, b, blocks, drops); + breakBlocks(e, p, tool, b, blocks, drops); }; } @ParametersAreNonnullByDefault - private void breakBlocks(Player p, ItemStack item, Block b, List blocks, List drops) { + private void breakBlocks(BlockBreakEvent e, Player p, ItemStack item, Block b, List blocks, List drops) { List blocksToDestroy = new ArrayList<>(); if (callExplosionEvent.getValue().booleanValue()) { @@ -96,7 +96,7 @@ public class ExplosiveTool extends SimpleSlimefunItem implements if (!event.isCancelled()) { for (Block block : blocksToDestroy) { - breakBlock(p, item, block, drops); + breakBlock(e, p, item, block, drops); } } } @@ -141,7 +141,7 @@ public class ExplosiveTool extends SimpleSlimefunItem implements } @ParametersAreNonnullByDefault - private void breakBlock(Player p, ItemStack item, Block b, List drops) { + private void breakBlock(BlockBreakEvent e, Player p, ItemStack item, Block b, List drops) { SlimefunPlugin.getProtectionManager().logAction(p, b, ProtectableAction.BREAK_BLOCK); Material material = b.getType(); @@ -149,10 +149,8 @@ public class ExplosiveTool extends SimpleSlimefunItem implements SlimefunItem sfItem = BlockStorage.check(b); if (sfItem != null && !sfItem.useVanillaBlockBreaking()) { - SlimefunBlockHandler handler = SlimefunPlugin.getRegistry().getBlockHandlers().get(sfItem.getId()); - - if (handler != null && !handler.onBreak(p, b, sfItem, UnregisterReason.PLAYER_BREAK)) { - drops.add(BlockStorage.retrieve(b)); + if (!sfItem.callItemHandler(BlockBreakHandler.class, handler -> handler.onPlayerBreak(e, item, drops))) { + drops.addAll(sfItem.getDrops(p)); } } else { b.breakNaturally(item); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GrapplingHook.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GrapplingHook.java index df0c0f600..57c8e8c47 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GrapplingHook.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GrapplingHook.java @@ -4,6 +4,7 @@ import java.util.UUID; import javax.annotation.ParametersAreNonnullByDefault; +import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.entity.Arrow; import org.bukkit.entity.Bat; @@ -54,7 +55,7 @@ public class GrapplingHook extends SimpleSlimefunItem { return e -> { Player p = e.getPlayer(); UUID uuid = p.getUniqueId(); - boolean isConsumed = consumeOnUse.getValue(); + boolean isConsumed = consumeOnUse.getValue() && p.getGameMode() != GameMode.CREATIVE; if (!e.getClickedBlock().isPresent() && !SlimefunPlugin.getGrapplingHookListener().isGrappling(uuid)) { e.cancel(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AutoCrafterListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AutoCrafterListener.java index 66be4a351..130f0b18d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AutoCrafterListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AutoCrafterListener.java @@ -14,6 +14,7 @@ import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.AbstractAutoCrafter; import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.EnhancedAutoCrafter; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Multimeter; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; /** @@ -50,6 +51,13 @@ public class AutoCrafterListener implements Listener { SlimefunItem block = slimefunBlock.get(); if (block instanceof AbstractAutoCrafter) { + Optional slimefunItem = e.getSlimefunItem(); + + if (!e.getPlayer().isSneaking() && slimefunItem.isPresent() && slimefunItem.get() instanceof Multimeter) { + // Allow Multimeters to pass through and do their job + return; + } + // Prevent blocks from being placed, food from being eaten, etc... e.cancel(); 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 79e71dd01..f8e754857 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 @@ -21,6 +21,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; @@ -29,9 +30,7 @@ import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; -import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.Slimefun; @@ -156,22 +155,10 @@ public class BlockListener implements Listener { } if (sfItem != null && !sfItem.useVanillaBlockBreaking()) { - SlimefunBlockHandler blockHandler = SlimefunPlugin.getRegistry().getBlockHandlers().get(sfItem.getId()); + sfItem.callItemHandler(BlockBreakHandler.class, handler -> handler.onPlayerBreak(e, item, drops)); - if (blockHandler != null) { - try { - if (!blockHandler.onBreak(e.getPlayer(), e.getBlock(), sfItem, UnregisterReason.PLAYER_BREAK)) { - e.setCancelled(true); - return; - } - } catch (Exception | LinkageError x) { - sfItem.error("Something went wrong while triggering a BlockHandler", x); - } - } else { - sfItem.callItemHandler(BlockBreakHandler.class, handler -> handler.onPlayerBreak(e, item, drops)); - if (e.isCancelled()) { - return; - } + if (e.isCancelled()) { + return; } drops.addAll(sfItem.getDrops()); @@ -191,6 +178,7 @@ public class BlockListener implements Listener { e.setDropItems(false); 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); } @@ -217,33 +205,27 @@ public class BlockListener implements Listener { SlimefunItem sfItem = BlockStorage.check(blockAbove); if (sfItem != null && !sfItem.useVanillaBlockBreaking()) { - SlimefunBlockHandler blockHandler = SlimefunPlugin.getRegistry().getBlockHandlers().get(sfItem.getId()); + /* + * We create a dummy here to pass onto the BlockBreakHandler. + * This will set the correct block context. + */ + BlockBreakEvent dummyEvent = new BlockBreakEvent(blockAbove, e.getPlayer()); + List drops = new ArrayList<>(); + drops.addAll(sfItem.getDrops(e.getPlayer())); - if (blockHandler != null) { - if (blockHandler.onBreak(e.getPlayer(), blockAbove, sfItem, UnregisterReason.PLAYER_BREAK)) { - blockAbove.getWorld().dropItemNaturally(blockAbove.getLocation(), BlockStorage.retrieve(blockAbove)); - blockAbove.setType(Material.AIR); - } - } else { - /* - * We create a dummy here to pass onto the BlockBreakHandler. - * This will set the correct block context. - */ - BlockBreakEvent dummyEvent = new BlockBreakEvent(blockAbove, e.getPlayer()); - List drops = new ArrayList<>(); - drops.addAll(sfItem.getDrops(e.getPlayer())); + sfItem.callItemHandler(BlockBreakHandler.class, handler -> handler.onPlayerBreak(dummyEvent, item, drops)); + blockAbove.setType(Material.AIR); - sfItem.callItemHandler(BlockBreakHandler.class, handler -> handler.onPlayerBreak(dummyEvent, item, drops)); - blockAbove.setType(Material.AIR); - - if (!dummyEvent.isCancelled() && dummyEvent.isDropItems()) { - for (ItemStack drop : drops) { - if (drop != null && drop.getType() != Material.AIR) { - blockAbove.getWorld().dropItemNaturally(blockAbove.getLocation(), drop); - } + if (!dummyEvent.isCancelled() && dummyEvent.isDropItems()) { + for (ItemStack drop : drops) { + if (drop != null && !drop.getType().isAir()) { + blockAbove.getWorld().dropItemNaturally(blockAbove.getLocation(), drop); } } } + + // Fixes #2944 - Don't forget to clear the Block Data + BlockStorage.clearBlockInfo(blockAbove); } } } @@ -251,10 +233,17 @@ public class BlockListener implements Listener { private int getBonusDropsWithFortune(@Nullable ItemStack item, @Nonnull Block b) { int amount = 1; - if (item != null) { - int fortuneLevel = item.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS); + if (item != null && !item.getType().isAir() && item.hasItemMeta()) { + /* + * Small performance optimization: + * ItemStack#getEnchantmentLevel() calls ItemStack#getItemMeta(), so if + * we are handling more than one Enchantment, we should access the ItemMeta + * directly and re use it. + */ + ItemMeta meta = item.getItemMeta(); + int fortuneLevel = meta.getEnchantLevel(Enchantment.LOOT_BONUS_BLOCKS); - if (fortuneLevel > 0 && !item.containsEnchantment(Enchantment.SILK_TOUCH)) { + if (fortuneLevel > 0 && !meta.hasEnchant(Enchantment.SILK_TOUCH)) { Random random = ThreadLocalRandom.current(); amount = Math.max(1, random.nextInt(fortuneLevel + 2) - 1); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ExplosionsListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ExplosionsListener.java index 7026f4f4c..7bfc54a75 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ExplosionsListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ExplosionsListener.java @@ -5,6 +5,7 @@ import java.util.Iterator; import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Material; import org.bukkit.block.Block; @@ -18,9 +19,7 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.core.attributes.WitherProof; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; import me.mrCookieSlime.Slimefun.api.BlockStorage; /** @@ -58,36 +57,26 @@ public class ExplosionsListener implements Listener { if (item != null) { blocks.remove(); - if (!(item instanceof WitherProof)) { - SlimefunBlockHandler blockHandler = SlimefunPlugin.getRegistry().getBlockHandlers().get(item.getId()); - boolean success = true; + if (!(item instanceof WitherProof) && !item.callItemHandler(BlockBreakHandler.class, handler -> handleExplosion(handler, block))) { + BlockStorage.clearBlockInfo(block); + block.setType(Material.AIR); + } + } + } + } - if (blockHandler != null) { - success = blockHandler.onBreak(null, block, item, UnregisterReason.EXPLODE); - } else { - item.callItemHandler(BlockBreakHandler.class, handler -> { - if (handler.isExplosionAllowed(block)) { - BlockStorage.clearBlockInfo(block); - block.setType(Material.AIR); + @ParametersAreNonnullByDefault + private void handleExplosion(BlockBreakHandler handler, Block block) { + if (handler.isExplosionAllowed(block)) { + BlockStorage.clearBlockInfo(block); + block.setType(Material.AIR); - List drops = new ArrayList<>(); - handler.onExplode(block, drops); + List drops = new ArrayList<>(); + handler.onExplode(block, drops); - for (ItemStack drop : drops) { - if (drop != null && !drop.getType().isAir()) { - block.getWorld().dropItemNaturally(block.getLocation(), drop); - } - } - } - }); - - return; - } - - if (success) { - BlockStorage.clearBlockInfo(block); - block.setType(Material.AIR); - } + for (ItemStack drop : drops) { + if (drop != null && !drop.getType().isAir()) { + block.getWorld().dropItemNaturally(block.getLocation(), drop); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java index 240f5ebcc..9575c86e0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java @@ -1,7 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.UUID; @@ -43,7 +42,7 @@ public class SoulboundListener implements Listener { ItemStack item = p.getInventory().getItem(slot); // Store soulbound items for later retrieval - if (SlimefunUtils.isSoulbound(item)) { + if (SlimefunUtils.isSoulbound(item, p.getWorld())) { items.put(slot, item); } } @@ -58,14 +57,7 @@ public class SoulboundListener implements Listener { } // Remove soulbound items from our drops - Iterator drops = e.getDrops().iterator(); - while (drops.hasNext()) { - ItemStack item = drops.next(); - - if (SlimefunUtils.isSoulbound(item)) { - drops.remove(); - } - } + e.getDrops().removeIf(itemStack -> SlimefunUtils.isSoulbound(itemStack, p.getWorld())); } @EventHandler diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java index ce21c0f35..9cd331b05 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java @@ -278,7 +278,14 @@ public class TalismanListener implements Listener { ItemStack item = e.getPlayer().getInventory().getItemInMainHand(); // We are going to ignore Silk Touch here - if (item.getType() != Material.AIR && item.getAmount() > 0 && !item.containsEnchantment(Enchantment.SILK_TOUCH)) { + if (item.getType() != Material.AIR && item.getAmount() > 0) { + ItemMeta meta = item.getItemMeta(); + + // Ignore Silk Touch Enchantment + if (meta.hasEnchant(Enchantment.SILK_TOUCH)) { + return; + } + Material type = e.getBlockState().getType(); // We only want to double ores @@ -286,7 +293,7 @@ public class TalismanListener implements Listener { Collection drops = e.getItems(); if (Talisman.trigger(e, SlimefunItems.TALISMAN_MINER, false)) { - int dropAmount = getAmountWithFortune(type, item.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS)); + int dropAmount = getAmountWithFortune(type, meta.getEnchantLevel(Enchantment.LOOT_BONUS_BLOCKS)); // Keep track of whether we actually doubled the drops or not boolean doubledDrops = false; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/PostSetup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/PostSetup.java index f0d7ecdb2..b44b87c03 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/PostSetup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/PostSetup.java @@ -27,7 +27,6 @@ import com.google.gson.JsonParser; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.GrindStone; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.MakeshiftSmeltery; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.OreCrusher; @@ -78,7 +77,6 @@ public final class PostSetup { } } - loadAutomaticCraftingChamber(); loadOreGrinderRecipes(); loadSmelteryRecipes(); @@ -128,14 +126,6 @@ public final class PostSetup { // @formatter:on } - private static void loadAutomaticCraftingChamber() { - AutomatedCraftingChamber crafter = (AutomatedCraftingChamber) SlimefunItems.AUTOMATED_CRAFTING_CHAMBER.getItem(); - - if (crafter != null) { - crafter.loadRecipes(); - } - } - private static void loadOreGrinderRecipes() { List grinderRecipes = new ArrayList<>(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java index ff62efaa3..fd8772d90 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java @@ -181,7 +181,7 @@ public final class ResearchSetup { register("electric_furnaces", 170, "Powered Furnace", 15, SlimefunItems.ELECTRIC_FURNACE); register("electric_ore_grinding", 171, "Crushing and Grinding", 20, SlimefunItems.ELECTRIC_ORE_GRINDER, SlimefunItems.ELECTRIC_INGOT_PULVERIZER); register("heated_pressure_chamber", 172, "Heated Pressure Chamber", 22, SlimefunItems.HEATED_PRESSURE_CHAMBER); - register("coal_generator", 173, "Coal Generator", 18, SlimefunItems.COAL_GENERATOR); + register("coal_generator", 173, "Coal Generator", 14, SlimefunItems.COAL_GENERATOR); register("bio_reactor", 173, "Bio-Reactor", 18, SlimefunItems.BIO_REACTOR); register("auto_enchanting", 174, "Automatic Enchanting and Disenchanting", 24, SlimefunItems.AUTO_ENCHANTER, SlimefunItems.AUTO_DISENCHANTER); register("auto_anvil", 175, "Automatic Anvil", 34, SlimefunItems.AUTO_ANVIL, SlimefunItems.AUTO_ANVIL_2); @@ -217,7 +217,8 @@ public final class ResearchSetup { register("cargo_basics", 205, "Cargo Basics", 30, SlimefunItems.CARGO_MOTOR, SlimefunItems.CARGO_MANAGER, SlimefunItems.CARGO_CONNECTOR_NODE); register("cargo_nodes", 206, "Cargo Setup", 30, SlimefunItems.CARGO_INPUT_NODE, SlimefunItems.CARGO_OUTPUT_NODE); register("electric_ingot_machines", 207, "Electric Ingot Fabrication", 18, SlimefunItems.ELECTRIC_GOLD_PAN, SlimefunItems.ELECTRIC_DUST_WASHER, SlimefunItems.ELECTRIC_INGOT_FACTORY); - register("high_tier_electric_ingot_machines", 209, "Super Fast Ingot Fabrication", 32, SlimefunItems.ELECTRIC_GOLD_PAN_3, SlimefunItems.ELECTRIC_DUST_WASHER_3, SlimefunItems.ELECTRIC_INGOT_FACTORY_3, SlimefunItems.ELECTRIC_ORE_GRINDER_2, SlimefunItems.ELECTRIC_ORE_GRINDER_3); + register("medium_tier_electric_ingot_machines", 208, "Fast Ingot Fabrication", 25, SlimefunItems.ELECTRIC_GOLD_PAN_2, SlimefunItems.ELECTRIC_DUST_WASHER_2, SlimefunItems.ELECTRIC_INGOT_FACTORY_2, SlimefunItems.ELECTRIC_ORE_GRINDER_2); + register("high_tier_electric_ingot_machines", 209, "Super Fast Ingot Fabrication", 32, SlimefunItems.ELECTRIC_GOLD_PAN_3, SlimefunItems.ELECTRIC_DUST_WASHER_3, SlimefunItems.ELECTRIC_INGOT_FACTORY_3, SlimefunItems.ELECTRIC_ORE_GRINDER_3); register("automated_crafting_chamber", 210, "Automated Crafting", 20, SlimefunItems.AUTOMATED_CRAFTING_CHAMBER); register("better_food_fabricator", 211, "Upgraded Food Fabrication", 28, SlimefunItems.FOOD_FABRICATOR_2, SlimefunItems.FOOD_COMPOSTER_2); register("reactor_access_port", 212, "Reactor Interaction", 18, SlimefunItems.REACTOR_ACCESS_PORT); @@ -242,7 +243,7 @@ public final class ResearchSetup { register("better_electric_crucibles", 231, "Hot Crucibles", 30, SlimefunItems.ELECTRIFIED_CRUCIBLE_2, SlimefunItems.ELECTRIFIED_CRUCIBLE_3); register("advanced_electric_smeltery", 232, "Advanced Electric Smeltery", 28, SlimefunItems.ELECTRIC_SMELTERY_2); register("advanced_farmer_android", 233, "Advanced Androids - Farmer", 30, SlimefunItems.PROGRAMMABLE_ANDROID_2_FARMER); - register("lava_generator", 234, "Lava Generator", 38, SlimefunItems.LAVA_GENERATOR); + register("lava_generator", 234, "Lava Generator", 16, SlimefunItems.LAVA_GENERATOR); register("nether_ice", 235, "Nether Ice Coolant", 45, SlimefunItems.NETHER_ICE, SlimefunItems.ENRICHED_NETHER_ICE, SlimefunItems.NETHER_ICE_COOLANT_CELL); register("nether_star_reactor", 236, "Nether Star Reactor", 60, SlimefunItems.NETHER_STAR_REACTOR); register("blistering_ingots", 237, "Blistering Radioactivity", 38, SlimefunItems.BLISTERING_INGOT, SlimefunItems.BLISTERING_INGOT_2, SlimefunItems.BLISTERING_INGOT_3); @@ -282,7 +283,10 @@ public final class ResearchSetup { register("bee_armor", 270, "Bee Armor", 24, SlimefunItems.BEE_HELMET, SlimefunItems.BEE_WINGS, SlimefunItems.BEE_LEGGINGS, SlimefunItems.BEE_BOOTS); register("wise_talisman", 271, "Talisman of the Wise", 20, SlimefunItems.TALISMAN_WISE); register("book_binder", 272, "Enchantment Book Binding", 26, SlimefunItems.BOOK_BINDER); - register("auto_crafting", 273, "Automatic Crafting", 30, SlimefunItems.VANILLA_AUTO_CRAFTER, SlimefunItems.ENHANCED_AUTO_CRAFTER); + register("auto_crafting", 273, "Automatic Crafting", 30, SlimefunItems.CRAFTING_MOTOR, SlimefunItems.VANILLA_AUTO_CRAFTER, SlimefunItems.ENHANCED_AUTO_CRAFTER, SlimefunItems.ARMOR_AUTO_CRAFTER); + register("produce_collector", 274, "Automatic Milking", 20, SlimefunItems.PRODUCE_COLLECTOR); + register("improved_generators", 275, "Improved Generators", 24, SlimefunItems.COAL_GENERATOR_2, SlimefunItems.LAVA_GENERATOR_2); + register("ingredients_and_cheese", 276, "Slimefun Cuisine", 5, SlimefunItems.SALT, SlimefunItems.WHEAT_FLOUR, SlimefunItems.HEAVY_CREAM, SlimefunItems.CHEESE, SlimefunItems.BUTTER); } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/SlimefunItemSetup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/SlimefunItemSetup.java index 98b5e897c..9bdaad135 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/SlimefunItemSetup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/SlimefunItemSetup.java @@ -40,6 +40,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.armor.LongFallBoo import io.github.thebusybiscuit.slimefun4.implementation.items.armor.Parachute; import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece; import io.github.thebusybiscuit.slimefun4.implementation.items.armor.StomperBoots; +import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.ArmorAutoCrafter; import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.EnhancedAutoCrafter; import io.github.thebusybiscuit.slimefun4.implementation.items.autocrafters.VanillaAutoCrafter; import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.Cooler; @@ -82,7 +83,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.generato import io.github.thebusybiscuit.slimefun4.implementation.items.electric.generators.MagnesiumGenerator; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.generators.SolarGenerator; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoAnvil; -import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoBreeder; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoBrewer; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoDrier; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber; @@ -109,9 +109,11 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.enchanting.AutoDisenchanter; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.enchanting.AutoEnchanter; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.enchanting.BookBinder; -import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.IronGolemAssembler; -import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.WitherAssembler; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.AutoBreeder; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.ExpCollector; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.IronGolemAssembler; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.ProduceCollector; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.entities.WitherAssembler; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.reactors.NetherStarReactor; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.reactors.NuclearReactor; import io.github.thebusybiscuit.slimefun4.implementation.items.elevator.ElevatorPlate; @@ -157,6 +159,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.medical.Splint; import io.github.thebusybiscuit.slimefun4.implementation.items.medical.Vitamins; import io.github.thebusybiscuit.slimefun4.implementation.items.misc.BasicCircuitBoard; import io.github.thebusybiscuit.slimefun4.implementation.items.misc.CoolantCell; +import io.github.thebusybiscuit.slimefun4.implementation.items.misc.GoldIngot; import io.github.thebusybiscuit.slimefun4.implementation.items.misc.OrganicFertilizer; import io.github.thebusybiscuit.slimefun4.implementation.items.misc.OrganicFood; import io.github.thebusybiscuit.slimefun4.implementation.items.misc.SteelThruster; @@ -715,47 +718,47 @@ public final class SlimefunItemSetup { new OreWasher(categories.basicMachines, SlimefunItems.ORE_WASHER).register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_24K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 24, SlimefunItems.GOLD_24K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_22K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_22K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 22, SlimefunItems.GOLD_22K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_20K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_20K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 20, SlimefunItems.GOLD_20K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_18K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_18K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 18, SlimefunItems.GOLD_18K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_16K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_16K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 16, SlimefunItems.GOLD_16K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_14K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_14K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 14, SlimefunItems.GOLD_14K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_12K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_12K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 12, SlimefunItems.GOLD_12K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_10K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_10K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 10, SlimefunItems.GOLD_10K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_8K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_8K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 8, SlimefunItems.GOLD_8K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_6K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_6K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 6, SlimefunItems.GOLD_6K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, SlimefunItems.GOLD_4K, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.resources, SlimefunItems.GOLD_4K, RecipeType.SMELTERY, + new GoldIngot(categories.resources, 4, SlimefunItems.GOLD_4K, RecipeType.SMELTERY, new ItemStack[] {SlimefunItems.GOLD_DUST, null, null, null, null, null, null, null, null}) .setUseableInWorkbench(true) .register(plugin); @@ -768,7 +771,7 @@ public final class SlimefunItemSetup { new ItemStack[] {new ItemStack(Material.QUARTZ_BLOCK), null, null, null, null, null, null, null, null}) .register(plugin); - new SlimefunItem(categories.technicalComponents, SlimefunItems.SOLAR_PANEL, RecipeType.ENHANCED_CRAFTING_TABLE, + new UnplaceableBlock(categories.technicalComponents, SlimefunItems.SOLAR_PANEL, RecipeType.ENHANCED_CRAFTING_TABLE, new ItemStack[] {new ItemStack(Material.GLASS), new ItemStack(Material.GLASS), new ItemStack(Material.GLASS), SlimefunItems.SILICON, SlimefunItems.SILICON, SlimefunItems.SILICON, SlimefunItems.FERROSILICON, SlimefunItems.FERROSILICON, SlimefunItems.FERROSILICON}) .register(plugin); @@ -1481,7 +1484,7 @@ public final class SlimefunItemSetup { new SlimefunItemStack(SlimefunItems.WITHER_PROOF_OBSIDIAN, 4)) .register(plugin); - new AncientPedestal(categories.magicalResources, SlimefunItems.ANCIENT_PEDESTAL, RecipeType.MAGIC_WORKBENCH, + new AncientPedestal(categories.magicalGadgets, SlimefunItems.ANCIENT_PEDESTAL, RecipeType.MAGIC_WORKBENCH, new ItemStack[] {new ItemStack(Material.OBSIDIAN), SlimefunItems.GOLD_8K, new ItemStack(Material.OBSIDIAN), null, new ItemStack(Material.STONE), null, new ItemStack(Material.OBSIDIAN), SlimefunItems.GOLD_8K, new ItemStack(Material.OBSIDIAN)}, new SlimefunItemStack(SlimefunItems.ANCIENT_PEDESTAL, 4)) .register(plugin); @@ -2476,7 +2479,7 @@ public final class SlimefunItemSetup { .register(plugin); new AutomatedCraftingChamber(categories.electricity, SlimefunItems.AUTOMATED_CRAFTING_CHAMBER, RecipeType.ENHANCED_CRAFTING_TABLE, - new ItemStack[] {null, new ItemStack(Material.CRAFTING_TABLE), null, SlimefunItems.CARGO_MOTOR, SlimefunItems.BLISTERING_INGOT_3, SlimefunItems.CARGO_MOTOR, null, SlimefunItems.ELECTRIC_MOTOR, null}) { + new ItemStack[] {null, null, null, null, new CustomItem(Material.BARRIER, "&4This Item has been disabled.", "&cIt will soon be removed!", "&cPlease switch over to the new", "&cAuto-Crafters from the Cargo Category."), null, null, null, null}) { @Override public int getEnergyConsumption() { @@ -2584,19 +2587,37 @@ public final class SlimefunItemSetup { new ElytraCap(categories.magicalArmor, SlimefunItems.ELYTRA_CAP, RecipeType.ARMOR_FORGE, new ItemStack[] {new ItemStack(Material.SLIME_BALL), new ItemStack(Material.SLIME_BALL), new ItemStack(Material.SLIME_BALL), SlimefunItems.ELYTRA_SCALE, SlimefunItems.ELYTRA_SCALE, SlimefunItems.ELYTRA_SCALE, new ItemStack(Material.SLIME_BALL), new ItemStack(Material.LEATHER_HELMET), new ItemStack(Material.SLIME_BALL)}) .register(plugin); + + new UnplaceableBlock(categories.cargo, SlimefunItems.CRAFTING_MOTOR, RecipeType.ENHANCED_CRAFTING_TABLE, + new ItemStack[] {new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.BLISTERING_INGOT_3, new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.REDSTONE_ALLOY, SlimefunItems.CARGO_MOTOR, SlimefunItems.REDSTONE_ALLOY, new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.BLISTERING_INGOT_3, new ItemStack(Material.CRAFTING_TABLE)}, + new SlimefunItemStack(SlimefunItems.CRAFTING_MOTOR, 2)) + .register(plugin); new VanillaAutoCrafter(categories.cargo, SlimefunItems.VANILLA_AUTO_CRAFTER, RecipeType.ENHANCED_CRAFTING_TABLE, - new ItemStack[] {null, SlimefunItems.BLISTERING_INGOT_3, null, new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.CARGO_MOTOR, new ItemStack(Material.CRAFTING_TABLE), null, SlimefunItems.ELECTRIC_MOTOR, null}) + new ItemStack[] {null, SlimefunItems.CARGO_MOTOR, null, new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.CRAFTING_MOTOR, new ItemStack(Material.CRAFTING_TABLE), null, SlimefunItems.ELECTRIC_MOTOR, null}) .setCapacity(256) .setEnergyConsumption(16) .register(plugin); new EnhancedAutoCrafter(categories.cargo, SlimefunItems.ENHANCED_AUTO_CRAFTER, RecipeType.ENHANCED_CRAFTING_TABLE, - new ItemStack[] {null, SlimefunItems.VANILLA_AUTO_CRAFTER, null, new ItemStack(Material.CRAFTING_TABLE), new ItemStack(Material.DISPENSER), new ItemStack(Material.CRAFTING_TABLE), null, SlimefunItems.CARGO_MOTOR, null}) + new ItemStack[] {null, SlimefunItems.CRAFTING_MOTOR, null, new ItemStack(Material.CRAFTING_TABLE), new ItemStack(Material.DISPENSER), new ItemStack(Material.CRAFTING_TABLE), null, SlimefunItems.CARGO_MOTOR, null}) .setCapacity(256) .setEnergyConsumption(16) .register(plugin); + new ArmorAutoCrafter(categories.cargo, SlimefunItems.ARMOR_AUTO_CRAFTER, RecipeType.ENHANCED_CRAFTING_TABLE, + new ItemStack[] {null, SlimefunItems.CRAFTING_MOTOR, null, new ItemStack(Material.DISPENSER), new ItemStack(Material.ANVIL), new ItemStack(Material.DISPENSER), new ItemStack(Material.CRAFTING_TABLE), SlimefunItems.ELECTRIC_MOTOR, new ItemStack(Material.CRAFTING_TABLE)}) + .setCapacity(256) + .setEnergyConsumption(32) + .register(plugin); + + new ProduceCollector(categories.electricity, SlimefunItems.PRODUCE_COLLECTOR, RecipeType.ENHANCED_CRAFTING_TABLE, + new ItemStack[] {null, new ItemStack(Material.HAY_BLOCK), null, new ItemStack(Material.BUCKET), SlimefunItems.MEDIUM_CAPACITOR, new ItemStack(Material.BUCKET), SlimefunItems.ALUMINUM_BRASS_INGOT, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.ALUMINUM_BRASS_INGOT}) + .setCapacity(256) + .setProcessingSpeed(1) + .setEnergyConsumption(16) + .register(plugin); + // @formatter:on } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java index fe498820e..662afda06 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java @@ -114,7 +114,11 @@ public enum HeadTexture { PIGLIN_HEAD("2882af1294a74023e6919a31d1a027310f2e142afb4667d230d155e7f21dbb41"), NECROTIC_SKULL("7953b6c68448e7e6b6bf8fb273d7203acd8e1be19e81481ead51f45de59a8"), VANILLA_AUTO_CRAFTER("80a4334f6a61e40c0c63deb665fa7b581e6eb259f7a3207ced7a1ff8bdc8a9f9"), - ENHANCED_AUTO_CRAFTER("5038298306a5e28584df39e88896917c38d40a326226d8c83070723c95798b24"); + ENHANCED_AUTO_CRAFTER("5038298306a5e28584df39e88896917c38d40a326226d8c83070723c95798b24"), + ARMOR_AUTO_CRAFTER("5cbd9f5ec1ed007259996491e69ff649a3106cf920227b1bb3a71ee7a89863f"), + EXCLAMATION_MARK("2e3f50ba62cbda3ecf5479b62fedebd61d76589771cc19286bf2745cd71e47c6"), + CARGO_MOTOR("8e47f99abcd645a3ef1122c9d850a981979f431ba293255c1680e91ab117ed35"), + CRAFTING_MOTOR("1003620899f1afa271e8e521ecbee2977a06c8529d3f389e8cc04af06d8c7940"); private final String texture; private final UUID uuid; 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 c7446de7f..aa381810d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -14,6 +14,7 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -79,15 +80,29 @@ public final class SlimefunUtils { /** * This method checks whether the given {@link ItemStack} is considered {@link Soulbound}. - * + * * @param item * The {@link ItemStack} to check for * @return Whether the given item is soulbound */ public static boolean isSoulbound(@Nullable ItemStack item) { - if (item == null || item.getType() == Material.AIR) { - return false; - } else { + return isSoulbound(item, null); + } + + /** + * This method checks whether the given {@link ItemStack} is considered {@link Soulbound}. + * If the provided item is a {@link SlimefunItem} then this method will also check that the item + * is enabled in the provided {@link World}. + * + * @param item + * The {@link ItemStack} to check for + * @param world + * The {@link World} to check if the {@link SlimefunItem} is enabled in if applicable. + * If {@code null} then this will not do a world check. + * @return Whether the given item is soulbound + */ + public static boolean isSoulbound(@Nullable ItemStack item, @Nullable World world) { + if (item != null && item.getType() != Material.AIR) { ItemMeta meta = item.hasItemMeta() ? item.getItemMeta() : null; if (hasSoulboundFlag(meta)) { @@ -97,13 +112,17 @@ public final class SlimefunUtils { SlimefunItem sfItem = SlimefunItem.getByItem(item); if (sfItem instanceof Soulbound) { - return !sfItem.isDisabled(); + if (world != null) { + return !sfItem.isDisabledIn(world); + } else { + return !sfItem.isDisabled(); + } } else if (meta != null) { return meta.hasLore() && meta.getLore().contains(SOULBOUND_LORE); } - return false; } + return false; } private static boolean hasSoulboundFlag(@Nullable ItemMeta meta) { @@ -111,9 +130,7 @@ public final class SlimefunUtils { PersistentDataContainer container = meta.getPersistentDataContainer(); NamespacedKey key = SlimefunPlugin.getRegistry().getSoulboundDataKey(); - if (container.has(key, PersistentDataType.BYTE)) { - return true; - } + return container.has(key, PersistentDataType.BYTE); } return false; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/UnbreakingAlgorithm.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/UnbreakingAlgorithm.java new file mode 100644 index 000000000..da9ce2c89 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/UnbreakingAlgorithm.java @@ -0,0 +1,56 @@ +package io.github.thebusybiscuit.slimefun4.utils; + +import java.util.function.IntFunction; + +import javax.annotation.Nonnull; + +import org.bukkit.enchantments.Enchantment; + +import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem; + +/** + * This a enum evaluating and indicating a {@link DamageableItem} 's chance to be damaged + * depending if it is a tool or an armor + * + * @author RobotHanzo + * + * @see DamageableItem + */ +public enum UnbreakingAlgorithm { + + /** + * For armor sets, unbreaking is capped at max. 40% effectiveness. + */ + ARMOR(lvl -> Math.random() >= 0.6 + (0.4 / (lvl + 1))), + + /** + * For tools, unbreaking is calculated like this. + * The effect increases indefinitely. + */ + TOOLS(lvl -> Math.random() >= 1.0 / (lvl + 1)); + + private final IntFunction function; + + UnbreakingAlgorithm(@Nonnull IntFunction function) { + this.function = function; + } + + /** + * This method will randomly decide if the item should be damaged or not + * based on the internal formula of this {@link UnbreakingAlgorithm}. + * If this method returns true, the item should not take damage. + * + * @param unbreakingLevel + * The {@link Integer} level of the unbreaking {@link Enchantment} + * + * @return Whether to save the item from taking damage + * + */ + public boolean evaluate(int unbreakingLevel) { + if (unbreakingLevel > 0) { + return function.apply(unbreakingLevel); + } else { + return false; + } + } +} diff --git a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/CustomItemSerializer.java b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/CustomItemSerializer.java deleted file mode 100644 index 6c6b7860f..000000000 --- a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/CustomItemSerializer.java +++ /dev/null @@ -1,117 +0,0 @@ -package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item; - -import java.util.Arrays; - -import java.util.Collections; -import java.util.List; - -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; - -/** - * An old remnant of CS-CoreLib. - * This will be removed once we updated everything. - * Don't look at the code, it will be gone soon, don't worry. - * - * @deprecated This was a horrible idea. Don't use it. - * - */ -@Deprecated -public class CustomItemSerializer { - - public enum ItemFlag { - - MATERIAL(0), - DATA(1), - AMOUNT(2), - DURABILITY(3), - ENCHANTMENTS(4), - ITEMMETA_DISPLAY_NAME(5), - ITEMMETA_LORE(6); - - private int weight; - - ItemFlag(int weight) { - this.weight = weight; - } - - public int getWeight() { - return this.weight; - } - - } - - private static ItemFlagComparator comparator = new ItemFlagComparator(); - - public static String serialize(ItemStack item, ItemFlag... flags) { - if (item == null) - return "NULL"; - List flaglist = Arrays.asList(flags); - - Collections.sort(flaglist, comparator); - - StringBuilder builder = new StringBuilder(); - - int i = 0; - for (ItemFlag flag : flags) { - if (i > 0) - builder.append(" "); - builder.append(flag.toString() + "="); - - switch (flag) { - case AMOUNT: { - builder.append(item.getAmount()); - break; - } - case DATA: { - builder.append((int) item.getData().getData()); - break; - } - case DURABILITY: { - builder.append((int) item.getDurability()); - break; - } - case ENCHANTMENTS: - for (Enchantment enchantment : Enchantment.values()) { - if (item.getEnchantments().containsKey(enchantment)) { - builder.append(enchantment.getName() + ":" + item.getEnchantmentLevel(enchantment)); - } else { - builder.append(enchantment.getName() + ":0"); - } - } - break; - case ITEMMETA_DISPLAY_NAME: { - if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) { - builder.append(item.getItemMeta().getDisplayName().replaceAll("\\u00a7", "&")); - } else { - builder.append("NONE"); - } - break; - } - case ITEMMETA_LORE: { - if (item.hasItemMeta() && item.getItemMeta().hasLore()) { - builder.append(item.getItemMeta().getLore().toString().replaceAll("\\u00a7", "&")); - } else { - builder.append("NONE"); - } - break; - } - case MATERIAL: { - builder.append(item.getType().toString()); - break; - } - default: - break; - } - - i++; - } - - return builder.toString(); - } - - public static boolean equals(ItemStack stack1, ItemStack stack2, ItemFlag... flags) { - return serialize(stack1, flags).equals(serialize(stack2, flags)); - } - -} diff --git a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/ItemFlagComparator.java b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/ItemFlagComparator.java deleted file mode 100644 index d49e1feb5..000000000 --- a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/Item/ItemFlagComparator.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item; - -import java.util.Comparator; - -import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.Item.CustomItemSerializer.ItemFlag; - -/** - * An old remnant of CS-CoreLib. - * This will be removed once we updated everything. - * Don't look at the code, it will be gone soon, don't worry. - * - * @deprecated This was a horrible idea. Don't use it. - * - */ -@Deprecated -public class ItemFlagComparator implements Comparator { - - @Override - public int compare(ItemFlag flag1, ItemFlag flag2) { - return flag1.getWeight() - flag2.getWeight(); - } - -} diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunBlockHandler.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunBlockHandler.java deleted file mode 100644 index 6009883e6..000000000 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunBlockHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -package me.mrCookieSlime.Slimefun.Objects; - -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; - -import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.UnregisterReason; - -/** - * A {@link SlimefunBlockHandler} handles breaking and placing of blocks. - * You can use this class to initialize block data but also to correctly - * destroy blocks. - * - * {@code SlimefunItem.registerBlockHandler(String, SlimefunBlockHandler); } - * - * @author TheBusyBiscuit - * - * @deprecated Please use the {@link BlockBreakHandler} instead. - * - */ -@Deprecated -@FunctionalInterface -public interface SlimefunBlockHandler { - - /** - * This method gets called when the {@link Block} is broken. - * The {@link Player} will be null if the {@link Block} exploded - * - * @param p - * The {@link Player} who broke the {@link Block} - * @param b - * The {@link Block} that was broken - * @param item - * The {@link SlimefunItem} that was stored in that {@link Block} - * @param reason - * The reason for the {@link Block} breaking - * @return Whether the {@link Event} should be cancelled - */ - boolean onBreak(Player p, Block b, SlimefunItem item, UnregisterReason reason); -} diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java index 77380f72b..054437b7e 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java @@ -40,7 +40,6 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.Placeable; import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive; import io.github.thebusybiscuit.slimefun4.core.attributes.Rechargeable; import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide; -import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.researching.Research; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem; @@ -805,14 +804,13 @@ public class SlimefunItem implements Placeable { Validate.notEmpty(handlers, "You cannot add zero handlers..."); Validate.noNullElements(handlers, "You cannot add any 'null' ItemHandler!"); + // Make sure they are added before the item was registered. if (state != ItemState.UNREGISTERED) { throw new UnsupportedOperationException("You cannot add an ItemHandler after the SlimefunItem was registered."); } for (ItemHandler handler : handlers) { - if (itemhandlers.put(handler.getIdentifier(), handler).isPresent()) { - warn("ItemHandler \"" + handler.getIdentifier().getSimpleName() + "\" has already been assigned to this item. It was overridden."); - } + itemhandlers.put(handler.getIdentifier(), handler); // Tickers are a special case (at the moment at least) if (handler instanceof BlockTicker) { @@ -1196,19 +1194,4 @@ public class SlimefunItem implements Placeable { return SlimefunPlugin.getRegistry().getPublicItemHandlers().computeIfAbsent(identifier, c -> new HashSet<>()); } - /** - * This has been deprecated. - * - * @deprecated Please use {@link #addItemHandler(ItemHandler...)} and {@link BlockBreakHandler} instead - * - * @param id - * The id - * @param handler - * The handler - */ - @Deprecated - public static void registerBlockHandler(@Nonnull String id, @Nullable me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler handler) { - SlimefunPlugin.getRegistry().getBlockHandlers().put(id, handler); - } - } \ No newline at end of file diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/UnregisterReason.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/UnregisterReason.java deleted file mode 100644 index 3361d2787..000000000 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/UnregisterReason.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.mrCookieSlime.Slimefun.Objects.SlimefunItem; - -import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; - -/** - * Defines how a block handled by Slimefun is being unregistered. - *

- * It is notably used by - * {@link me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler#onBreak(org.bukkit.entity.Player, org.bukkit.block.Block, SlimefunItem, UnregisterReason)}. - * - * @author TheBusyBiscuit - * - * @deprecated This enum is no longer needed - * - * @see SlimefunBlockHandler - * - */ -@Deprecated -public enum UnregisterReason { - - /** - * An explosion destroys the block. - */ - EXPLODE, - - /** - * A player breaks the block. - */ - PLAYER_BREAK, - - /** - * An android miner breaks the block. - */ - ANDROID_DIG - -} diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java index ee71c86dc..62265bca7 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; @@ -24,7 +25,9 @@ import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.events.AsyncMachineProcessCompleteEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; +import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; @@ -47,8 +50,9 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock, private static final int[] BORDER_IN = { 9, 10, 11, 12, 18, 21, 27, 28, 29, 30 }; private static final int[] BORDER_OUT = { 14, 15, 16, 17, 23, 26, 32, 33, 34, 35 }; - public static Map processing = new HashMap<>(); - public static Map progress = new HashMap<>(); + // These will be replaced by proper recipe handler + public static Map processing = new ConcurrentHashMap<>(); + public static Map progress = new ConcurrentHashMap<>(); protected final List recipes = new ArrayList<>(); @@ -57,27 +61,35 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock, private int processingSpeed = -1; @ParametersAreNonnullByDefault - public AContainer(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + protected AContainer(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); createPreset(this, getInventoryTitle(), this::constructMenu); - registerBlockHandler(item.getItemId(), (p, b, tool, reason) -> { - BlockMenu inv = BlockStorage.getInventory(b); + addItemHandler(onBlockBreak()); + } - if (inv != null) { - inv.dropItems(b.getLocation(), getInputSlots()); - inv.dropItems(b.getLocation(), getOutputSlots()); + @Nonnull + protected BlockBreakHandler onBlockBreak() { + return new SimpleBlockBreakHandler() { + + @Override + public void onBlockBreak(Block b) { + BlockMenu inv = BlockStorage.getInventory(b); + + if (inv != null) { + inv.dropItems(b.getLocation(), getInputSlots()); + inv.dropItems(b.getLocation(), getOutputSlots()); + } + + progress.remove(b); + processing.remove(b); } - - progress.remove(b); - processing.remove(b); - return true; - }); + }; } @ParametersAreNonnullByDefault - public AContainer(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { + protected AContainer(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { this(category, item, recipeType, recipe); this.recipeOutput = recipeOutput; } diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AGenerator.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AGenerator.java index 0ae282211..0e7c840e0 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AGenerator.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AGenerator.java @@ -21,8 +21,10 @@ import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.events.AsyncGeneratorProcessCompleteEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemState; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.AbstractEnergyProvider; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; @@ -76,22 +78,29 @@ public abstract class AGenerator extends AbstractEnergyProvider { } }; - registerBlockHandler(item.getItemId(), (p, b, tool, reason) -> { - BlockMenu inv = BlockStorage.getInventory(b); - - if (inv != null) { - inv.dropItems(b.getLocation(), getInputSlots()); - inv.dropItems(b.getLocation(), getOutputSlots()); - } - - progress.remove(b.getLocation()); - processing.remove(b.getLocation()); - return true; - }); - + addItemHandler(onBlockBreak()); registerDefaultFuelTypes(); } + @Nonnull + protected BlockBreakHandler onBlockBreak() { + return new SimpleBlockBreakHandler() { + + @Override + public void onBlockBreak(Block b) { + BlockMenu inv = BlockStorage.getInventory(b); + + if (inv != null) { + inv.dropItems(b.getLocation(), getInputSlots()); + inv.dropItems(b.getLocation(), getOutputSlots()); + } + + progress.remove(b.getLocation()); + processing.remove(b.getLocation()); + } + }; + } + private void constructMenu(BlockMenuPreset preset) { for (int i : border) { preset.addItem(i, ChestMenuUtils.getBackground(), ChestMenuUtils.getEmptyClickHandler()); diff --git a/src/main/resources/languages/messages_en.yml b/src/main/resources/languages/messages_en.yml index 40403c4c3..6f1daac08 100644 --- a/src/main/resources/languages/messages_en.yml +++ b/src/main/resources/languages/messages_en.yml @@ -38,7 +38,7 @@ placeholderapi: guide: locked: 'LOCKED' work-in-progress: 'This feature is not fully finished yet!' - + locked-category: - 'To unlock this category you will' - 'need to unlock all items from the' @@ -64,11 +64,11 @@ guide: cheat: no-multiblocks: '&4You cannot cheat in Multiblocks, you have to build them!' - + pages: previous: 'Previous page' next: 'Next page' - + back: title: 'Back' guide: 'Go back to the Slimefun Guide' @@ -86,11 +86,11 @@ guide: - '&7the language in which Slimefun' - '&7will be presented to you. Items' - '&7cannot be translated for now.' - + translations: name: '&aIs something missing?' lore: 'Click to add your own translation' - + title: main: 'Slimefun Guide' settings: 'Settings & Info' @@ -101,7 +101,7 @@ guide: bugs: 'Bug Reports' source: 'Source Code' versions: 'Installed versions' - + credits: commit: 'Commit' commits: 'Commits' @@ -151,9 +151,22 @@ messages: recipe-set: '&aYou have successfully set the recipe for this machine.' recipe-removed: '&eYou have successfully removed the recipe from this machine. You can set a new recipe at any time!' no-recipes: '&cWe could not find any valid recipes for the item you are holding.' - missing-chest: '&cMissing chest! Auto Crafters need to be placed above a chest.' + missing-chest: '&cMissing chest! Auto-Crafters need to be placed above a chest.' select: 'Select this recipe' - remove: 'Remove this recipe' + temporarily-disabled: '&eThis Auto-Crafter was temporarily disabled. You can re-enable it at any time!' + re-enabled: '&eThis Auto-Crafter was re-enabled!' + + tooltips: + enabled: + - '&aThis recipe is currently enabled' + - '' + - '&eLeft Click &7to temporarily disable the recipe' + - '&eRight Click &7to remove this recipe' + disabled: + - '&cThis recipe is currently disabled' + - '' + - '&eLeft Click &7to re-enable this recipe' + - '&eRight Click &7to remove this recipe' talisman: anvil: '&a&oYour Talisman saved your tool from breaking' @@ -180,17 +193,17 @@ messages: fail: '&cYou cannot enchant this item.' no-enchantment: '&cCouldn''t find any applicable enchantment for this item.' success: '&aYou have successfully applied a random applicable enchantment to this item.' - + research: start: '&7The Ancient Spirits whisper mysterious words into your ear!' progress: '&7You start to wonder about &b%research% &e(%progress%)' - + tape-measure: no-anchor: '&cYou need to set an anchor before you can start to measure!' wrong-world: '&cYour anchor seems to be in a different world!' distance: '&7Measurement taken. &eDistance: %distance%' anchor-set: '&aSuccessfully set the anchor:&e %anchor%' - + climbing-pick: dual-wielding: '&4You need to hold Climbing Picks in both hands to use them!' wrong-material: '&cYou cannot climb this surface. Check your Slimefun Guide for more info!' @@ -204,7 +217,7 @@ messages: no-iron-golem-heal: '&cThat is not an Iron Ingot. You cannot use this to heal Iron Golems!' link-prompt: '&eClick here:' diet-cookie: '&eYou are starting to feel very light...' - + fortune-cookie: - '&7Help me, I am trapped in a Fortune Cookie Factory!' - '&7You will die tomorrow... by a Creeper' @@ -230,6 +243,7 @@ machines: full-inventory: '&eSorry, my inventory is too full!' in-use: '&cThis Block''s inventory is currently opened by a different Player.' ignition-chamber-no-flint: '&cIgnition Chamber is missing Flint and Steel.' + inventory-empty: '&6You have successfully constructed this Multiblock. Proceed by placing items inside the dispenser and click me again to craft the item you want!' ANCIENT_ALTAR: not-enough-pedestals: '&4The Altar is not surrounded by the needed amount of Pedestals &c(%pedestals% / 8)' @@ -242,7 +256,7 @@ machines: HOLOGRAM_PROJECTOR: enter-text: '&7Please enter your desired Hologram Text into your Chat. &r(Color Codes are supported!)' inventory-title: 'Hologram Editor' - + ELEVATOR: no-destinations: '&4No destinations found' pick-a-floor: '&3- Pick a floor -' @@ -250,26 +264,26 @@ machines: click-to-teleport: '&eClick &7to teleport to this floor:' enter-name: '&7Please enter your desired floor name into your Chat. &r(Color Codes are supported!)' named: '&2Successfully named this floor: &r%floor%' - + TELEPORTER: teleporting: '&3Teleporting...' teleported: '&3Teleported!' cancelled: '&4Teleportation cancelled!' invulnerability: '&b&lYou have been given 30 seconds of Invulnerability!' - + gui: title: 'Your waypoints' tooltip: 'Click to teleport' time: 'Estimated time' - + GPS_CONTROL_PANEL: title: 'GPS - Control Panel' transmitters: 'Transmitter Overview' waypoints: 'Waypoint Overview' - + CARGO_NODES: must-be-placed: '&4Must be placed onto a chest or machine!' - + INDUSTRIAL_MINER: no-fuel: '&cYour Industrial Miner ran out of fuel! Put your fuel into the chest above.' piston-facing: '&cYour Industrial Miner requires pistons to face upwards!' @@ -279,7 +293,7 @@ machines: full-chest: '&cThe Chest of your Industrial Miner is full!' no-permission: '&4You do not seem to have permission to operate an Industrial Miner here!' finished: '&eYour Industrial Miner has finished! It obtained a total of %ores% ore(s)!' - + anvil: not-working: '&4You cannot use Slimefun items in an anvil!' mcmmo-salvaging: '&4You cannot salvage Slimefun items!' @@ -325,12 +339,12 @@ inventory: android: started: '&7Your Android resumed running its script' stopped: '&7Your Android has paused its script' - + scripts: already-uploaded: '&4This script has already been uploaded.' editor: 'Script Editor' too-long: '&cThe script is too long to edit!' - + instructions: START: '&2Start Script' REPEAT: '&9Repeat Script' diff --git a/src/main/resources/languages/researches_en.yml b/src/main/resources/languages/researches_en.yml index b4d121cc4..ad7975eff 100644 --- a/src/main/resources/languages/researches_en.yml +++ b/src/main/resources/languages/researches_en.yml @@ -188,6 +188,7 @@ slimefun: cargo_basics: Cargo Basics cargo_nodes: Cargo Setup electric_ingot_machines: Electric Ingot Fabrication + medium_tier_electric_ingot_machines: Fast Ingot Fabrication high_tier_electric_ingot_machines: Super Fast Ingot Fabrication automated_crafting_chamber: Automated Crafting better_food_fabricator: Upgraded Food Fabrication @@ -250,3 +251,6 @@ slimefun: bee_armor: Bee Armor book_binder: Enchantment Book Binding auto_crafting: Automatic Crafting + produce_collector: Automatic Milking + improved_generators: Improved Generators + ingredients_and_cheese: Slimefun Cuisine diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/mocks/DamageableMock.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/mocks/DamageableMock.java new file mode 100644 index 000000000..93199ce04 --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/mocks/DamageableMock.java @@ -0,0 +1,34 @@ +package io.github.thebusybiscuit.slimefun4.testing.mocks; + +import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.Category; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +public class DamageableMock extends SlimefunItem implements DamageableItem { + + private final boolean itemDamageable; + + @ParametersAreNonnullByDefault + public DamageableMock(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, boolean damageable) { + super(category, item, recipeType, recipe); + itemDamageable = damageable; + } + + @Override + public boolean isDamageable() { + return itemDamageable; + } + + @Override + public void damageItem(@Nonnull Player p, @Nullable ItemStack item) { + DamageableItem.super.damageItem(p, item); + } +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestDamageableItem.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestDamageableItem.java new file mode 100644 index 000000000..2a5b43866 --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestDamageableItem.java @@ -0,0 +1,101 @@ +package io.github.thebusybiscuit.slimefun4.testing.tests.items; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.testing.TestUtilities; +import io.github.thebusybiscuit.slimefun4.testing.mocks.DamageableMock; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.Category; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +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.DisplayName; +import org.junit.jupiter.api.Test; + +import javax.annotation.Nullable; + +class TestDamageableItem { + + private static SlimefunPlugin plugin; + private static ServerMock server; + + @BeforeAll + public static void load() { + server = MockBukkit.mock(); + plugin = MockBukkit.load(SlimefunPlugin.class); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + public static DamageableMock getDummyItem(String id, boolean damageable, @Nullable Enchantment enchantment, @Nullable Integer enchantmentLevel) { + Category category = TestUtilities.getCategory(plugin, "damageable_item_test"); + SlimefunItemStack stack = new SlimefunItemStack("DAMAGEABLE_PICKAXE_" + id, Material.DIAMOND_PICKAXE, "&4This pickaxe can break", "&6It appears, it breaks, but most importantly, it tests."); + if (enchantment != null && enchantmentLevel != null) { + ItemMeta im = stack.getItemMeta(); + im.addEnchant(enchantment, enchantmentLevel, true); + stack.setItemMeta(im); + } + DamageableMock item = new DamageableMock(category, stack, RecipeType.NULL, new ItemStack[9], damageable); + item.register(plugin); + return item; + } + + @Test + @DisplayName("Test DamageableItem actually damages") + void testDamageableItemDamagesItem() { + DamageableMock unDamageableItem = getDummyItem("UDM", false, null, null); + // Check if damageable is successfully registered + Assertions.assertFalse(unDamageableItem.isDamageable()); + + Player player = server.addPlayer(); + ItemStack unDamageableDummy = unDamageableItem.getItem().clone(); + unDamageableItem.damageItem(player, unDamageableDummy); + Assertions.assertTrue(unDamageableDummy.hasItemMeta()); + // Check if the item is not damaged as we intended it to not be + Assertions.assertEquals(((Damageable) unDamageableDummy.getItemMeta()).getDamage(), 0); + + DamageableMock damageableItem = getDummyItem("DM", true, null, null); + // Check if damageable is successfully registered + Assertions.assertTrue(damageableItem.isDamageable()); + + ItemStack damageableDummy = damageableItem.getItem().clone(); + damageableItem.damageItem(player, damageableDummy); + Assertions.assertTrue(damageableDummy.hasItemMeta()); + // Check if the item is damaged as we intended it to not be + Assertions.assertEquals(((Damageable) damageableDummy.getItemMeta()).getDamage(), 1); + } + + @Test + @DisplayName("Test if DamageableItem cares about unbreaking levels") + void testDamageableItemCaresUnbreaking() { + DamageableMock noUnbreakingItem = getDummyItem("NU", true, null, null); + DamageableMock iiiUnbreakingItem = getDummyItem("IIIU", true, Enchantment.DURABILITY, 3); + DamageableMock xUnbreakingItem = getDummyItem("XU", true, Enchantment.DURABILITY, 10); + ItemStack noUnbreakingItemIS = noUnbreakingItem.getItem().clone(); + ItemStack iiiUnbreakingItemIS = iiiUnbreakingItem.getItem().clone(); + ItemStack xUnbreakingItemIS = xUnbreakingItem.getItem().clone(); + Player player = server.addPlayer(); + for (int i = 0; i < 500; ++i) { + noUnbreakingItem.damageItem(player, noUnbreakingItemIS); + noUnbreakingItem.damageItem(player, iiiUnbreakingItemIS); + noUnbreakingItem.damageItem(player, xUnbreakingItemIS); + } + Assertions.assertTrue(noUnbreakingItemIS.hasItemMeta()); + Assertions.assertTrue(iiiUnbreakingItemIS.hasItemMeta()); + Assertions.assertTrue(xUnbreakingItemIS.hasItemMeta()); + Assertions.assertTrue(((Damageable) xUnbreakingItemIS.getItemMeta()).getDamage() < ((Damageable) iiiUnbreakingItemIS.getItemMeta()).getDamage()); + Assertions.assertTrue(((Damageable) iiiUnbreakingItemIS.getItemMeta()).getDamage() < ((Damageable) noUnbreakingItemIS.getItemMeta()).getDamage()); + } + +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAbstractRecipe.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAbstractRecipe.java similarity index 99% rename from src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAbstractRecipe.java rename to src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAbstractRecipe.java index 61b6f6219..3291a3812 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAbstractRecipe.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAbstractRecipe.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.testing.tests.items.auto_crafters; +package io.github.thebusybiscuit.slimefun4.testing.tests.items.autocrafters; import java.util.Arrays; import java.util.HashSet; diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAutoCrafter.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAutoCrafter.java similarity index 74% rename from src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAutoCrafter.java rename to src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAutoCrafter.java index 548940f5b..8770c1045 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/auto_crafters/TestAutoCrafter.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/autocrafters/TestAutoCrafter.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.testing.tests.items.auto_crafters; +package io.github.thebusybiscuit.slimefun4.testing.tests.items.autocrafters; import javax.annotation.Nonnull; @@ -69,6 +69,58 @@ class TestAutoCrafter { Assertions.assertTrue(inv.containsAtLeast(result, 1)); } + @Test + @DisplayName("Test crafting a valid ShapelessRecipe") + void testDisabledRecipe() { + NamespacedKey key = new NamespacedKey(plugin, "disabled_recipe_test"); + ItemStack result = new CustomItem(Material.DIAMOND, "&bAmazing Diamond :o"); + ShapelessRecipe recipe = new ShapelessRecipe(key, result); + recipe.addIngredient(new MaterialChoice(Material.GOLD_NUGGET)); + + AbstractRecipe abstractRecipe = AbstractRecipe.of(recipe); + AbstractAutoCrafter crafter = getVanillaAutoCrafter(); + InventoryMock inv = new ChestInventoryMock(null, 9); + + // Test enabled Recipe + abstractRecipe.setEnabled(true); + inv.addItem(new ItemStack(Material.GOLD_NUGGET)); + Assertions.assertTrue(crafter.craft(inv, abstractRecipe)); + Assertions.assertFalse(inv.contains(Material.GOLD_NUGGET, 1)); + Assertions.assertTrue(inv.containsAtLeast(result, 1)); + + inv.clear(); + + // Test disabled Recipe + abstractRecipe.setEnabled(false); + inv.addItem(new ItemStack(Material.GOLD_NUGGET)); + Assertions.assertFalse(crafter.craft(inv, abstractRecipe)); + Assertions.assertTrue(inv.contains(Material.GOLD_NUGGET, 1)); + Assertions.assertFalse(inv.containsAtLeast(result, 1)); + } + + @Test + @DisplayName("Test resource leftovers when crafting") + void testResourceLeftovers() { + NamespacedKey key = new NamespacedKey(plugin, "resource_leftovers_test"); + ItemStack result = new CustomItem(Material.DIAMOND, "&9Diamond. Nuff said."); + ShapelessRecipe recipe = new ShapelessRecipe(key, result); + recipe.addIngredient(new MaterialChoice(Material.HONEY_BOTTLE)); + recipe.addIngredient(new MaterialChoice(Material.HONEY_BOTTLE)); + + AbstractRecipe abstractRecipe = AbstractRecipe.of(recipe); + AbstractAutoCrafter crafter = getVanillaAutoCrafter(); + InventoryMock inv = new ChestInventoryMock(null, 9); + + inv.addItem(new ItemStack(Material.HONEY_BOTTLE, 2)); + Assertions.assertTrue(crafter.craft(inv, abstractRecipe)); + + Assertions.assertFalse(inv.contains(Material.HONEY_BOTTLE, 2)); + Assertions.assertTrue(inv.containsAtLeast(result, 1)); + + // Check for leftovers + Assertions.assertTrue(inv.contains(Material.GLASS_BOTTLE, 2)); + } + @Test @DisplayName("Test crafting an invalid ShapelessRecipe") void testInvalidShapelessRecipe() { diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestSoulboundListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestSoulboundListener.java index 4d2aec92c..babbb3722 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestSoulboundListener.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestSoulboundListener.java @@ -1,11 +1,16 @@ package io.github.thebusybiscuit.slimefun4.testing.tests.listeners; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundItem; +import io.github.thebusybiscuit.slimefun4.testing.TestUtilities; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import org.bukkit.Material; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.inventory.ItemStack; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -17,7 +22,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; -public class TestSoulboundListener { +class TestSoulboundListener { private static SlimefunPlugin plugin; private static ServerMock server; @@ -36,7 +41,8 @@ public class TestSoulboundListener { @ParameterizedTest @ValueSource(booleans = { true, false }) - public void testItemDrop(boolean soulbound) { + @DisplayName("Test if the soulbound item is dropped or not") + void testItemDrop(boolean soulbound) { PlayerMock player = server.addPlayer(); ItemStack item = new CustomItem(Material.DIAMOND_SWORD, "&4Cool Sword"); SlimefunUtils.setSoulbound(item, soulbound); @@ -50,7 +56,32 @@ public class TestSoulboundListener { @ParameterizedTest @ValueSource(booleans = { true, false }) - public void testItemRecover(boolean soulbound) { + @DisplayName("Test if soulbound item is dropped if disabled") + void testItemDropIfItemDisabled(boolean enabled) { + PlayerMock player = server.addPlayer(); + + SlimefunItemStack item = new SlimefunItemStack("SOULBOUND_ITEM_" + (enabled ? "ENABLED" : "DISABLED"), Material.DIAMOND_SWORD, "&5Soulbound Sword"); + SoulboundItem soulboundItem = new SoulboundItem(TestUtilities.getCategory(plugin, "soulbound"), item, RecipeType.NULL, new ItemStack[9]); + soulboundItem.register(plugin); + + if (!enabled) { + SlimefunPlugin.getWorldSettingsService().setEnabled(player.getWorld(), soulboundItem, false); + } + + player.getInventory().setItem(0, item); + player.setHealth(0); + + server.getPluginManager().assertEventFired(EntityDeathEvent.class, event -> { + // If the item is enabled, we don't want it to drop. + return enabled == !event.getDrops().contains(item); + }); + SlimefunPlugin.getRegistry().getEnabledSlimefunItems().remove(soulboundItem); + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @DisplayName("Test if soulbound item is returned to player") + void testItemRecover(boolean soulbound) { PlayerMock player = server.addPlayer(); ItemStack item = new CustomItem(Material.DIAMOND_SWORD, "&4Cool Sword"); SlimefunUtils.setSoulbound(item, soulbound);